diff --git a/config.yaml.example b/config.yaml.example index 7d8a3b1..ee0fa11 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 98d02a4..57230be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [ diff --git a/radio-settings.json b/radio-settings.json index e3f74ad..3810aa7 100644 --- a/radio-settings.json +++ b/radio-settings.json @@ -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 } } -} +} \ No newline at end of file diff --git a/repeater/config.py b/repeater/config.py index 3ce3670..7f09568 100644 --- a/repeater/config.py +++ b/repeater/config.py @@ -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: diff --git a/repeater/web/api_endpoints.py b/repeater/web/api_endpoints.py index 4776038..c18bc13 100644 --- a/repeater/web/api_endpoints.py +++ b/repeater/web/api_endpoints.py @@ -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: diff --git a/tests/test_radio_config.py b/tests/test_radio_config.py new file mode 100644 index 0000000..04a0a05 --- /dev/null +++ b/tests/test_radio_config.py @@ -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 \ No newline at end of file