src.nth.io/

summaryrefslogtreecommitdiff
path: root/openhop
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-08-01 19:24:23 -0500
committerLuke Hoersten <[email protected]>2026-08-01 19:24:23 -0500
commit254577c537e20dbd2e365c63773e2010f3c8374d (patch)
tree11d3d5fe024cd7e33f1eb8f62e86cd079c72c403 /openhop
parenteab57497338b290cf10454a7272c6890de12e824 (diff)
openhop/repeater: fixes from first real deploy on ubuntuHEADmain
- create spi/gpio groups and udev device rules (Raspberry Pi OS ships them, Ubuntu does not) - hardware CS via spidev (cs_pin -1); a manual cs_pin on a kernel-owned CE line fails EBUSY - align RAK6421 pin maps with upstream's hardware catalog: en_pins for the FEM enables, gpiod backend on chip 0 (catalog says chip 1, wrong for Pi 4) - us RF preset is now MeshCore USA/Canada (Recommended): 910.525 / SF7 / BW62.5 / CR5 - set storage.storage_dir; omit mqtt_brokers entirely (any key enables the MQTT pusher) - fetch radio-settings/radio-presets json into the data dir (pip omits them and the web setup wizard's hardware step is empty without them) - config deployed 0660 with a vaultable repeater.security block so the daemon can persist credentials and re-templates no longer wipe them - sudoers rule so the web UI can restart the service after config changes
Diffstat (limited to 'openhop')
-rw-r--r--openhop/repeater/defaults/main.yaml7
-rw-r--r--openhop/repeater/tasks/main.yaml60
-rw-r--r--openhop/repeater/templates/config.yaml.j226
-rw-r--r--openhop/repeater/vars/boards/rak6421-slot1.yaml16
-rw-r--r--openhop/repeater/vars/boards/rak6421-slot2.yaml16
-rw-r--r--openhop/repeater/vars/rf/us.yaml7
6 files changed, 117 insertions, 15 deletions
diff --git a/openhop/repeater/defaults/main.yaml b/openhop/repeater/defaults/main.yaml
index d26e73e..b72dcfb 100644
--- a/openhop/repeater/defaults/main.yaml
+++ b/openhop/repeater/defaults/main.yaml
@@ -12,6 +12,13 @@ repeater_venv_dir: "/opt/openhop_repeater/venv"
repeater_config_dir: "/etc/openhop_repeater"
repeater_data_dir: "/var/lib/openhop_repeater"
+# --- Web UI credentials (set from vaulted host_vars) ---
+# When null the daemon generates a jwt_secret at startup and requires its setup
+# wizard before login; anything the wizard writes into config.yaml is lost on
+# the next re-template, so real deployments should set both here.
+repeater_admin_password: null
+repeater_jwt_secret: null
+
# --- Node identity ---
repeater_node_name: "{{inventory_hostname}}"
repeater_latitude: 0.0
diff --git a/openhop/repeater/tasks/main.yaml b/openhop/repeater/tasks/main.yaml
index b24044c..3d47ef4 100644
--- a/openhop/repeater/tasks/main.yaml
+++ b/openhop/repeater/tasks/main.yaml
@@ -28,6 +28,32 @@
- "python3-dev"
- "build-essential"
+# Raspberry Pi OS ships the spi/gpio groups and udev rules via
+# raspberrypi-sys-mods; Ubuntu images have neither, so create them here.
+- name: create spi and gpio device access groups
+ become: yes
+ group:
+ name: "{{item}}"
+ system: "yes"
+ loop:
+ - "spi"
+ - "gpio"
+
+- name: grant spi and gpio groups access to their devices
+ become: yes
+ copy:
+ dest: "/etc/udev/rules.d/90-spi-gpio-groups.rules"
+ content: |
+ SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
+ SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660"
+ mode: "0644"
+ register: udev_result
+
+- name: apply udev rules to existing devices
+ become: yes
+ shell: "udevadm control --reload-rules && udevadm trigger --subsystem-match=spidev --subsystem-match=gpio"
+ when: udev_result.changed
+
- name: add openhop repeater user
become: yes
user:
@@ -67,6 +93,22 @@
when: not repeater_installed.stat.exists
notify: restart openhop-repeater
+# The pip package does not ship these repo-root files, but the web UI's setup
+# wizard reads them from the storage dir; without them the hardware step lists
+# no options and cannot proceed.
+- name: install web UI radio settings and presets
+ become: yes
+ get_url:
+ url: "{{repeater_repo}}/raw/{{repeater_version}}/{{item}}"
+ dest: "{{repeater_data_dir}}/{{item}}"
+ owner: "{{repeater_user}}"
+ group: "{{repeater_user}}"
+ mode: "0644"
+ force: "{{not repeater_installed.stat.exists}}"
+ loop:
+ - "radio-settings.json"
+ - "radio-presets.json"
+
- name: stamp installed openhop_repeater version
become: yes
copy:
@@ -75,6 +117,9 @@
mode: "0644"
when: not repeater_installed.stat.exists
+# Group-writable so the daemon can persist its generated jwt_secret and the
+# setup-wizard credentials back into the config. Note the trade-off: every
+# re-template wipes those daemon-written keys and restarts the service.
- name: configure openhop repeater
become: yes
template:
@@ -82,9 +127,22 @@
dest: "{{repeater_config_dir}}/config.yaml"
owner: "root"
group: "{{repeater_user}}"
- mode: "0640"
+ mode: "0660"
notify: restart openhop-repeater
+# The web UI restarts the daemon after config changes via
+# "sudo --non-interactive systemctl restart openhop-repeater"; the daemon's
+# code expects exactly this sudoers file to exist.
+- name: allow repeater user to restart its own service
+ become: yes
+ copy:
+ dest: "/etc/sudoers.d/openhop-repeater"
+ content: |
+ {{repeater_user}} ALL=(root) NOPASSWD: /usr/bin/systemctl restart openhop-repeater
+ {{repeater_user}} ALL=(root) NOPASSWD: /usr/bin/systemctl restart openhop-repeater.service
+ mode: "0440"
+ validate: "visudo -cf %s"
+
- name: install openhop-repeater systemd service
become: yes
template:
diff --git a/openhop/repeater/templates/config.yaml.j2 b/openhop/repeater/templates/config.yaml.j2
index ddd2772..a26cd05 100644
--- a/openhop/repeater/templates/config.yaml.j2
+++ b/openhop/repeater/templates/config.yaml.j2
@@ -12,6 +12,20 @@ repeater:
score_threshold: 0.3
send_advert_interval_hours: 10
allow_discovery: true
+{% if repeater_admin_password or repeater_jwt_secret %}
+ security:
+{% if repeater_admin_password %}
+ admin_password: "{{ repeater_admin_password }}"
+{% endif %}
+{% if repeater_jwt_secret %}
+ jwt_secret: "{{ repeater_jwt_secret }}"
+{% endif %}
+{% endif %}
+
+# The daemon defaults its auth DB and telemetry to /var/lib/openhop_repeater
+# regardless of other paths, so point it at the role-managed data dir.
+storage:
+ storage_dir: "{{ repeater_data_dir }}"
radio_type: sx1262
@@ -24,6 +38,9 @@ sx1262:
irq_pin: {{ repeater_sx1262_irq_pin }}
txen_pin: {{ repeater_sx1262_txen_pin }}
rxen_pin: {{ repeater_sx1262_rxen_pin }}
+ en_pins: {{ repeater_sx1262_en_pins | to_json }}
+ gpio_chip: {{ repeater_sx1262_gpio_chip }}
+ use_gpiod_backend: {{ repeater_sx1262_use_gpiod_backend | string | lower }}
en_pin: -1
txled_pin: -1
rxled_pin: -1
@@ -41,11 +58,10 @@ radio:
preamble_length: {{ repeater_preamble_length }}
implicit_header: false
-# Observer/telemetry disabled — repeater-only deployment.
-mqtt_brokers:
- iata_code: "Test"
- status_interval: 300
- brokers: []
+# Observer/telemetry disabled: repeater-only deployment. The daemon enables
+# its MQTT pusher whenever an mqtt_brokers/letsmesh/mqtt key is present, even
+# with an empty broker list (and then warns on every noise-floor sample), so
+# the section must be omitted entirely.
glass:
enabled: false
diff --git a/openhop/repeater/vars/boards/rak6421-slot1.yaml b/openhop/repeater/vars/boards/rak6421-slot1.yaml
index 464a194..c49ea9a 100644
--- a/openhop/repeater/vars/boards/rak6421-slot1.yaml
+++ b/openhop/repeater/vars/boards/rak6421-slot1.yaml
@@ -3,13 +3,23 @@
# Source: meshtastic/firmware bin/config.d/lora-RAK6421-13302-slot1.yaml
repeater_sx1262_bus_id: 0 # spidev0.0
repeater_sx1262_cs_id: 0 # CE0 (BCM 8)
-repeater_sx1262_cs_pin: 8
+# -1 = hardware CS via spidev. The kernel spi driver owns the CE line, so a
+# manual cs_pin here fails with EBUSY; manual CS is only for boards whose NSS
+# is wired to a non-CE GPIO (e.g. Waveshare).
+repeater_sx1262_cs_pin: -1
repeater_sx1262_reset_pin: 16
repeater_sx1262_busy_pin: 24
repeater_sx1262_irq_pin: 22 # DIO1
repeater_sx1262_use_dio3_tcxo: true
repeater_sx1262_dio3_tcxo_voltage: 1.8
repeater_sx1262_use_dio2_rf: true
-repeater_sx1262_txen_pin: 12 # SKY66122 FEM enable (verify TX vs RX)
-repeater_sx1262_rxen_pin: 13 # SKY66122 FEM enable (verify TX vs RX)
+# Per upstream's hardware catalog for this board: both SKY66122 FEM enables are
+# asserted together via en_pins and DIO2 does the actual TX/RX switching.
+repeater_sx1262_txen_pin: -1
+repeater_sx1262_rxen_pin: -1
+repeater_sx1262_en_pins: [12, 13]
+repeater_sx1262_use_gpiod_backend: true
+# Upstream's catalog says gpio_chip 1, but on a Pi 4 the 40-pin header is
+# gpiochip0 (chip 1 is the 8-line firmware expansion GPIO).
+repeater_sx1262_gpio_chip: 0
repeater_sx1262_is_waveshare: false
diff --git a/openhop/repeater/vars/boards/rak6421-slot2.yaml b/openhop/repeater/vars/boards/rak6421-slot2.yaml
index 71a5fb9..ce03662 100644
--- a/openhop/repeater/vars/boards/rak6421-slot2.yaml
+++ b/openhop/repeater/vars/boards/rak6421-slot2.yaml
@@ -3,13 +3,23 @@
# Source: meshtastic/firmware bin/config.d/lora-RAK6421-13302-slot2.yaml
repeater_sx1262_bus_id: 0 # spidev0.1
repeater_sx1262_cs_id: 1 # CE1 (BCM 7)
-repeater_sx1262_cs_pin: 7
+# -1 = hardware CS via spidev. The kernel spi driver owns the CE line, so a
+# manual cs_pin here fails with EBUSY; manual CS is only for boards whose NSS
+# is wired to a non-CE GPIO (e.g. Waveshare).
+repeater_sx1262_cs_pin: -1
repeater_sx1262_reset_pin: 24
repeater_sx1262_busy_pin: 19
repeater_sx1262_irq_pin: 18 # DIO1
repeater_sx1262_use_dio3_tcxo: true
repeater_sx1262_dio3_tcxo_voltage: 1.8
repeater_sx1262_use_dio2_rf: true
-repeater_sx1262_txen_pin: 26 # SKY66122 FEM enable (verify TX vs RX)
-repeater_sx1262_rxen_pin: 23 # SKY66122 FEM enable (verify TX vs RX)
+# Per upstream's hardware catalog for this board: both SKY66122 FEM enables are
+# asserted together via en_pins and DIO2 does the actual TX/RX switching.
+repeater_sx1262_txen_pin: -1
+repeater_sx1262_rxen_pin: -1
+repeater_sx1262_en_pins: [26, 23]
+repeater_sx1262_use_gpiod_backend: true
+# Upstream's catalog says gpio_chip 1, but on a Pi 4 the 40-pin header is
+# gpiochip0 (chip 1 is the 8-line firmware expansion GPIO).
+repeater_sx1262_gpio_chip: 0
repeater_sx1262_is_waveshare: false
diff --git a/openhop/repeater/vars/rf/us.yaml b/openhop/repeater/vars/rf/us.yaml
index e20b1c6..2eecc99 100644
--- a/openhop/repeater/vars/rf/us.yaml
+++ b/openhop/repeater/vars/rf/us.yaml
@@ -1,11 +1,12 @@
---
-# MeshCore US 902-928 MHz preset.
+# MeshCore "USA/Canada (Recommended)" community preset: 910.525 / SF7 / BW62.5
+# / CR5. The wide alternate is SF11 / BW250 / CR5.
# WARNING: every node on your mesh MUST share identical frequency / bandwidth /
# spreading_factor / coding_rate. Confirm these against your local
# MeshCore US community settings before deploying.
repeater_frequency_hz: 910525000 # 910.525 MHz
-repeater_bandwidth_hz: 250000
-repeater_spreading_factor: 11 # 7-12
+repeater_bandwidth_hz: 62500
+repeater_spreading_factor: 7 # 7-12
repeater_coding_rate: 5 # 5-8
repeater_preamble_length: 32
repeater_tx_power_dbm: 22 # SX1262 register max; SKY66122 boosts to ~1W