Skip to content

RAK3401 1W repeater compact LoRa update chain

Status: published prerelease for controlled, recoverable lab use. On 29-Aug-2026 steps 1-9 completed on the target RAK3401 and are byte-for-byte identical to the corresponding transitions in the physically passed fd98bc90 chain. The replacement step 10 installs the current b40d2e6c endpoint. That replacement package and the hardened current runner have passed offline reconstruction plus both required bootloader simulators, but have not had a new physical end-to-end run. Use the lab gate and keep local DFU/SWD recovery available. Multi-hop and alternate-bandwidth estimates remain planning data, not physical qualification.

The prerelease is rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c. Use this asset:

RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
  • prerelease ZIP SHA-256: 8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4
  • inner SHA256SUMS.txt SHA-256: 29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a
  • endpoint image SHA-256: a7761da8af1f2447d7f01d72e2b1456b99c0e874a165d6483f48ab1f58e8d905
  • endpoint EndF body hash: 1FA6AAA3C28D8BD8

--verify-only remains the safe default. Live preflight and installation of this prerelease require --accept-test-candidate. That switch bypasses only the prerelease-status gate; it does not bypass the archive, identity, bootloader, route, watchdog, reachability, or post-boot checks.

Exact destination

This chain is intentionally specific to one firmware lineage and hardware target:

  • target ID: 2FA509C1
  • hardware: RAK_3401
  • role: RAK_3401_repeater_lora_ota_no_external_sensors
  • start: v1.16.7.0-c1caa5ad, EndF 71F4026CBE4B8B74
  • endpoint source: MeshCore b40d2e6cb2a185db740175e8b161ea71736d19d9
  • endpoint label: v1.17.1.5-halo-keymind-cascade-dev-b40d2e6c
  • endpoint packed version: 0x01110105
  • deployment target key: 63d8df6387eaffd2e25db7d2a8ad967a65202182a48d681d7e7a9260f917280d

Retained and omitted hardware support

Despite the legacy no_external_sensors target name, the endpoint retains the RAK12501 GPS provider and the common INA3221, INA219, INA226, and INA260 I2C voltage/current monitors. Install RAK12501 in sensor slot A. Do not use slot D with the RAK13302 1 W radio because the GPS reset/PPS signals overlap the radio's BUSY/DIO1 wiring.

The reduced profile omits optional external environmental and ranging devices: AHTX0, BME280, BMP280, SHTC3, SHT4x, LPS22HB, MLX90614, VL53L0X, BME680, BMP085, RAK12035, and BME680 BSEC. Board radio, display, buttons, battery telemetry, GPS, and the INA monitor family remain. No additional “remove I2C temperature” image is needed: the optional I2C temperature/environment drivers are already in the omitted set, while removing the remaining I2C/INA support would discard desired voltage/current monitoring and create another firmware identity without helping this route.

Do not use the chain on another target ID, hardware family, starting image, or firmware body hash. The runner checks all four.

Why Preview 5 and the old staging ceiling work

The deployed bootloader is not changed and no package uses the newer expanded 0xED000 ceiling. Each package remains bottom-aligned below the old bootloader's 0xD4000 scan ceiling. Its detools workspace is page-aligned and must satisfy:

0x26000 + inplace_memory <= staged_package_start < 0xD4000

The old bootloader already reads and validates that encoded detools geometry before its first destructive write. The previous application receiver was the limiting part: it reserved a fixed 0x98000 workspace before accepting a download. Step 1 therefore remains byte-for-byte identical to the physically passed 89,844-byte package. Step 2 is a 46,364-byte fixed-workspace package that installs the compatibility receiver. Later receivers stage above the real EndF image extent and let the bootloader enforce each package's selected workspace.

Every package in this candidate is an ordinary application container using format ABI 2 and in-place codec 2. The historical bridge applications scan the bootloader capability marker byte-by-byte, so they recognize the released Preview 5 RAK4631 marker at absolute address 0xFCDEA even though it is two bytes off a word boundary. The b40d2e6c endpoint retains that exact Preview 5 application-update fallback. Its separately compiled internal bootloader-update feature remains unavailable on Preview 5 because privileged self-update requires a current ABI 3 marker with the exact storage profile. Installing this chain therefore does not replace the bootloader, and the final application can still accept a future valid ABI-2/codec-2 application update.

The schema-2 exhaustive search checked 17,371 candidate geometries and found 121 feasible forward edges in the declared 33-image inventory. Fourteen ten-package routes tied for the minimum package count; the selected route has the smallest total container size among them. That minimum applies to the declared inventory, not every conceivable firmware image. The selected route is:

Step From To Workspace Package Margin
1 1.16.7.0 1.16.7.9 0x98000 89,844 0
2 1.16.7.9 1.16.7.10 0x98000 46,364 40,960
3 1.16.7.10 1.16.8.7 0x91000 106,029 12,288
4 1.16.8.7 1.16.9.105 0x89000 149,927 0
5 1.16.9.105 1.16.9.110 0x8E000 111,312 16,384
6 1.16.9.110 1.16.9.113 0x8F000 120,624 4,096
7 1.16.9.113 1.16.9.117 0x8B000 139,637 0
8 1.16.9.117 1.16.10.0 0x7E000 174,202 20,480
9 1.16.10.0 1.17.1.3 0x6B000 271,569 0
10 1.17.1.3 1.17.1.5 0x74000 174,744 61,440

Total mOTA transfer data is 1,384,252 bytes. ROUTE.json, CHAIN.csv, and validation-results.json in the bundle pin the exact geometry and image hash for every transition.

Steps 1-9 are byte-for-byte identical to the transitions that passed on the physical RAK3401. The replacement step 10, from the physically reached FE65A6135A1E7B3F v1.17.1.3 body to 1FA6AAA3C28D8BD8, has offline qualification only. All ten packages passed independent container checks, zero-filled and erased-workspace reconstruction, the OTAFIX 2.4.1 Preview 5 simulator, and the current OTAFIX simulator. Those results prove the package bytes and apply geometry; they are not a physical claim for the replacement step 10 or the hardened runner.

Host requirements

Install Python 3.9 or newer, meshcli 1.6.0 or newer, the current motatool, and an OTA-enabled Full Companion or repeater that can seed mOTA files.

Verify offline

No password or device is needed:

python3 tools/lora_ota/rak3401_mota_chain.py \
  --bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip \
  --work-dir ./rak3401-mota-chain-work \
  --motatool /path/to/motatool \
  --verify-only

When given the ZIP, the runner checks its pinned outer hash and binds the extraction cache to that exact archive. An explicitly supplied extracted root starts at the pinned inner checksum list. In either case it checks complete inner coverage, all ten manifests, continuity, image anchors, both recovery images, and every container with motatool. Passing this command means offline and simulator qualification only; it does not unlock live use.

Guarded prerelease lab command

These direct-link arguments match the physical transition setup, but the current package replaces step 10 and the current host runner contains later cleanup and recovery fixes. Neither has been cleanly rerun through the whole chain. Keep local USB/SWD recovery available. The candidate switch bypasses only the prerelease-status block; all identity, checksum, bootloader, route, watchdog, reachability, and post-boot gates still run.

Restore the test start locally with recovery/test-start/RAK3401-test-start-v1.16.7-c1caa5ad.uf2, then run:

export MESHCORE_ADMIN_PASSWORD='password'

python3 tools/lora_ota/rak3401_mota_chain.py \
  --bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip \
  --work-dir ./rak3401-mota-chain-work \
  --controller-serial /dev/ttyACM0 \
  --source-tcp 192.168.1.51:5001 \
  --source-cli-tcp 192.168.1.51:5002 \
  --source-shares-controller \
  --target-key 63d8df63 \
  --temp-radio 909.950,500,5,5,120 \
  --ota-hops 0 \
  --legacy-full-airtime \
  --motatool /path/to/motatool \
  --accept-test-candidate \
  --yes

Keep the work directory. Rerunning the same command resumes only when the live EndF body hash matches an exact chain node. Never manually skip a package. --legacy-full-airtime temporarily sets the destination airtime factor to zero and restores it at the endpoint. Use that option only where the selected frequency and local duty-cycle rules permit a full transmit budget; omit it otherwise.

Legacy fd98bc90 physical and SWD qualification

The earlier local candidate remains the exact physical evidence source for this route:

RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-fd98bc90.zip
  • candidate ZIP SHA-256: c0b33f4568985e8b2b8dc99411295907212cf2bad21764b6333d5e0ba298fd61
  • inner SHA256SUMS.txt SHA-256: 3f8c4af8096b96a4aa6506825c387cc8a06f74d5213a29c9387bd11689546881
  • endpoint image SHA-256: 31c182c888ceb1135e5afb2376610d93cee2e807b556c838e07fd4486c79d095
  • endpoint EndF body hash: 9179B98432895924

All ten exact fd98bc90 transitions completed on the RAK3401 with its deployed 0.9.2-OTAFIX2.4 bootloader and a Heltec V4 source at 909.950 MHz / 500 kHz / SF5 / CR5. Every intermediate EndF hash matched. Independent SWD readback then matched the endpoint application, original bootloader, SoftDevice/MBR, and UICR byte-for-byte. Steps 1-9 are also the exact first nine packages in the b40d2e6c prerelease. The two candidates have different step 10 packages and endpoints, so the physical result and step-10 timing must not be attributed to b40d2e6c.

The first ready to install transfer ages were:

Step Blocks Transfer
1 88 598 s
2 45 42 s
3 103 81 s
4 146 105 s
5 109 76 s
6 118 94 s
7 136 102 s
8 170 127 s
9 264 187 s
10 169 193 s

The physically observed bulk-transfer total for those exact legacy packages is 1,605 seconds (26m45s). The log came from the pre-cleanup-fix runner revision. Step 1 was measured before the host began forcing source RXPS off and is therefore a conservative outlier. A new complete run with that fix is projected at roughly 18–19 minutes of bulk transfer, but that projection is not a substitute for the measured total. Allow about 70–75 minutes direct at BW500 for validation, ten transfers, installs/reboots, retained-store checks, and final restoration.

The legacy step 10's longer per-block time was not a V4 TempRadio delay: the source RXPS readback was off. Its v1.17.1.3 requester uses non-overlapping adaptive flights and a five-second recovery floor and logged more block reloads than step 8. The V4 itself intentionally schedules its local radio handoff after 1.5 seconds so the normal-channel command reply can drain; the host waits three seconds and then checks the live TempRadio state.

Live progress and speed sanity checks

The runner watches motatool serve -v locally, so its quarter-progress reports do not consume LoRa airtime. At BW500 direct, physically measured steps 2–9 averaged about 1.1–1.4 newly requested payload blocks per second; the legacy step 10 averaged about 0.88 blocks/s, and step 1's 0.15 blocks/s was the known pre-RXPS-fix outlier. Use those only as bench sanity ranges, not guarantees. Relays, narrower bandwidths, interference, and recovery reads lower the rate.

Healthy output keeps advancing from [download] passive source progress x/y to source read all y payload blocks, followed by destination status ready to install y/y (100%) for the same manifest ID. Aggregate reads above the unique block count indicate recovery/retry traffic. A radio-scaled no-progress window causes one destination status check; the transfer timeout leaves the partial download staged for an exact-package resume. Stop and diagnose instead of installing if the manifest changes, progress remains flat through those checks, the final receiver count is not y/y, or the measured rate is far below the matching bandwidth/hop estimate without an understood RF cause.

For planning only, scale the conservative measured fd98bc90 26m45s bulk baseline by the qualified adaptive-preamble airtime and by each additional relay transmission:

Bandwidth, SF5/CR5 Direct / 0 relays 1 relay 2 relays
500 kHz 26m45s 53m30s 1h20m15s
250 kHz 46m51s 1h33m42s 2h20m33s
125 kHz 1h27m03s 2h54m07s 4h21m11s
62.5 kHz 2h54m07s 5h48m15s 8h42m23s

These are transfer-only estimates, not measured alternate-bandwidth or relay results. Packet loss, relay contention, discovery, administrative commands, staged verification, and ten reboot cycles add time. The bandwidth factors are not simple powers of two because the adaptive wire preamble is 128 symbols at 500 kHz, 64 at 250 kHz, and 32 at 125/62.5 kHz.

Manual operation

The automated runner is preferred because it binds every transition to the expected MID and body hash. The manual details below document the checks used during qualification and recovery; they are not permission to skip the runner's candidate gate. Keep a written copy of each original setting and never skip a step even when a later package appears in ota ls.

1. Verify and extract the asset

sha256sum RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
unzip RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
cd RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c
sha256sum -c SHA256SUMS.txt
for package in motas/*.mota; do motatool verify "$package" || exit 1; done

The outer hash must be 8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4, and the SHA-256 of the extracted SHA256SUMS.txt must be 29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a. Do not continue after any checksum or verification error.

2. Record and prepare the destination

Before changing anything, save the complete replies from:

ver
ota self
get bootloader.ver
get system.watchdog
get radio.rxps
powersaving
get rxdelay
get af
ota config

The starting ota self hash must be 71F4026CBE4B8B74, the target must be 2FA509C1, and the hardware must be RAK_3401. Disable the nRF52 system watchdog before the chain:

set system.watchdog off

The hardware watchdog cannot stop immediately. The RAK will reset once within about 60 seconds. Reconnect, wait at least 90 seconds from the command, and require get system.watchdog to report > off before continuing.

For a direct bench link, apply these temporary transfer guardrails to the RAK after every bridge reboot:

powersaving off
set radio.rxps off
set rxdelay 0
ota config hops 0
tempradio 909.950,500,5,5,120

Where local duty-cycle rules permit it, set af 0 can also remove the legacy firmware's saved airtime wait during this bounded maintenance window. Record get af first and restore that exact value after the final step. Current firmware grants the bounded TempRadio transfer budget without overwriting the saved airtime factor.

powersaving off is an isolation guardrail, not the fix for the observed failure. The actual source failure was an oversized USB CDC reply; current firmware splits those reads. Some historical bridge builds may report RXPS as unsupported while already operating continuously. Verify the reported state and do not substitute a normal reboot for the one-time watchdog reset. Direct tests use hops 0: hops 1 makes the target echo each source response despite there being no intermediate relay, increasing half-duplex loss and legacy three-second retries.

The exact candidate cannot use fast RXPS throughout because its historical bridge applications predate the v1.17.1.5 adaptive-preamble contract. Keep RXPS off for all ten steps whether using SF5/BW250 or the faster direct-bench SF5/BW500 tuple. After the final endpoint is proven, a later update whose target, controller, source, and relays are all v1.17.1.5 or newer can use the generic LoRa OTA runner and its qualified adaptive-preamble boundaries.

Put the source on the identical TempRadio tuple. A current ASCII-first Full Companion recognizes motatool's initial ota folder on line directly:

motatool serve \
  --dir ./motas \
  --serial /dev/ttyACM1 \
  --baud 115200 \
  -v

The same command also works with older Full Companion firmware in binary mode, where the idle parser recognizes the identical preamble. Restart motatool for every step so the source emits a fresh catalog advert. Leave it running during the download and stop it with Ctrl-C only after the destination reports ready to install.

3. Install all ten packages in order

Step Manifest ID Before hash Expected version After hash
1 C147BCEF 71F4026CBE4B8B74 1.16.7.9 42BC53A64288E845
2 BEB578FC 42BC53A64288E845 1.16.7.10 1AD2AA8B6C478DA6
3 6D8EF814 1AD2AA8B6C478DA6 1.16.8.7 D709A25308BC1831
4 40AC4CEB D709A25308BC1831 1.16.9.105 457BEDA5E6406C81
5 F6C8890B 457BEDA5E6406C81 1.16.9.110 E76BFA205634CAB8
6 82405E89 E76BFA205634CAB8 1.16.9.113 65328FC8A1FBED2D
7 80F40DB6 65328FC8A1FBED2D 1.16.9.117 AF7532E13337FADD
8 DBF6310D AF7532E13337FADD 1.16.10.0 22002359BBDFA76E
9 C2979E08 22002359BBDFA76E 1.17.1.3 FE65A6135A1E7B3F
10 B5011420 FE65A6135A1E7B3F 1.17.1.5 1FA6AAA3C28D8BD8

For each row, first prove that ota self exactly matches its Before hash, then inspect the manager before changing it:

ota status

Proceed to discovery only after that status is reconciled. When the manager is idle, run:

ota ls
ota pull MANIFEST_ID flash
ota status

If the first status reports this row's MID as partial, use ota pull with that same MID to resume it. If this row's MID is already ready, do not pull or cancel it; continue with the install checks below. If status reports the immediately previous row's MID as verifying staged blocks, wait through the bounded discovery window; require either that exact MID to become ready to install or the manager to become no download, and re-prove the current Before hash. Cancel only the same previous MID while it is visibly attached and ready. If it has become idle, send no cancel. Stop on any other MID, failed or incomplete state, ambiguous reply, or timeout. Never issue a blind ota cancel: it can discard a resumable or unrelated session.

Repeat ota status at a restrained interval until it says ready to install. If the manifest is initially absent, wait for a fresh source advert, run ota ls again, or restart motatool; do not pull a different ID. Stop the seeder and install:

get system.watchdog
ota install

The watchdog reply must still be > off. Allow the current default five-minute readiness window for the USB/LoRa identity to return; automation probes every 10 seconds and returns early as soon as the exact body is visible. Then run ver, ota self, and ota help. Require the row's exact After hash, require bootloader: apply OK, and require rescue install <hash16> in the help before exposing that bridge to the next package. This rescue-help gate applies to steps 1-9; step 10 is the endpoint and is not exposed to another package in this chain. A normal-radio ota status response of no download proves only that the manager is idle; legacy internal-flash firmware does not durably erase a retained container by accepting an IDLE ota cancel. Do not claim otherwise. When the same expected manifest reports verifying staged blocks after the next TempRadio starts, wait only for the bounded discovery window and require that it become ready to install or no download; stop on a changed ID, failed/incomplete state, or timeout. Prove the exact running body before cancelling an attached completed session. OTAFIX consumes the approval word during a successful install, so a retained endpoint container is inert and a later valid pull replaces it. Reapply the transfer guardrails and TempRadio tuple, restart motatool, and continue with the next row.

If the board boots but ota self says the EndF is invalid, stop. Only when ota status still identifies that row's complete staged package may the guarded ota rescue install BEFORE_HASH command be used. It is not a force option and must use that row's exact Before hash. If the board does not boot, recover the documented start/recovery UF2 locally over USB. Step 10 is the endpoint and has no successor package in this chain. If its EndF is invalid, use local DFU/SWD rather than attempting an unqualified LoRa package.

4. Intermediate relays and restoration

On every managed intermediate relay, save get rxdelay and get txdelay, then use the airtime-scaled collision window during the maintenance session:

set rxdelay 0
set txdelay 0.3

Current OTA transfer packets honor that configured relay txdelay; setting 0.3 gives competing relays a small randomized, airtime-scaled forwarding window. The source's manifest-fragment gap independently follows active packet airtime and duty spacing, clamped to 100-1000 ms. These two delays solve different problems.

After step 10 is proven, restore each saved destination and relay value exactly, including rxdelay, RXPS, CPU power saving, af, ota config hops, and relay timing. Current full-parser repeater firmware accepts normalradio and restores the saved tuple after replying on the temporary channel:

normalradio

The b40d2e6c endpoint supports that command. If a recovery build unexpectedly replies Unknown command, shorten its lease to one minute with the same tuple:

tempradio 909.950,500,5,5,1

Wait for the lease to expire, return the controller and ordinary source to their saved normal channels, and then re-enable the RAK system watchdog:

set system.watchdog on
get system.watchdog

A Full Companion source is the exception: its local TCP console supports normalradio and the automated runner uses it before restoring the shared Binary API radio tuple.

Final lab success requires version v1.17.1.5-halo-keymind-cascade-dev-b40d2e6c, body hash 1FA6AAA3C28D8BD8, target 2FA509C1, hardware RAK_3401, and get system.watchdog reporting > on. If a relay cannot be restored before its TempRadio lease ends, wait for it to return to the normal channel and restore its saved rxdelay and txdelay there.

Two-relay deployment

The first nine direct package transitions above were physically observed as the byte-identical first nine transitions of the legacy chain. Replacement step 10, this hardened host-runner revision, and multi-hop operation are not cleanly end-to-end physically qualified. A controlled, recoverable multi-hop lab can exercise two intermediate relays by listing them farthest-to-nearest and using three OTA hops. Run a non-mutating preflight first; even preflight requires the prerelease candidate override because the command connects to live devices:

python3 tools/lora_ota/rak3401_mota_chain.py \
  --bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip \
  --work-dir ./rak3401-mota-chain-work \
  --controller-serial /dev/ttyACM0 \
  --source-tcp SOURCE_IP:5001 \
  --source-cli-tcp SOURCE_IP:5002 \
  --source-shares-controller \
  --target-key 63d8df63 \
  --relay 'Far Repeater' \
  --relay 'Near Repeater' \
  --temp-radio 909.950,250,5,5,120 \
  --ota-hops 3 \
  --motatool /path/to/motatool \
  --accept-test-candidate \
  --preflight-only

After preflight succeeds, rerun with --yes in place of --preflight-only. Confirm that the selected frequency and bandwidth are legal at the deployment location.

Rescue and completion

The guarded ota rescue install <base_body_hash> command is present after step 1 and remains available through the step-9 bridge. It is not a force command: it refuses a valid normal EndF, a base mismatch, wrong target/hardware, or invalid payload. A failure before step 1 completes still requires local USB recovery because the deployed start image predates the rescue command. The final b40d2e6c endpoint uses the stricter shared-slot profile; its invalid-EndF recovery is local DFU/SWD, not the rescue command.

Before the first mutation, the runner saves the destination's RXPS periods, CPU power-saving state, RX flood delay, airtime factor, and OTA hop reach in the persistent work directory, flushing both the file and its directory entry before changing a guardrail. It also saves the managed source's exact RXPS preference in the chain-root source-rxps-settings.json before disabling it. Every step reuses that protected record, so a rerun after a killed host process restores the chain-start preference instead of treating the temporary off state as original. The record is accepted only for the same CLI endpoint. The runner retires that chain-root record only after it has proved the source is back on its normal radio and restored the exact preference at the verified endpoint. The runner reads the destination, controller, source, and relay versions before applying the same RXPS policy as the generic runner. The mixed historical chain therefore verifies radio.rxps off after every bridge reboot; a future all-v1.17.1.5-or-newer SF5/BW250 chain would instead keep RXPS on under the qualified level-8/preamble-64 boundary. It also verifies powersaving off and rxdelay 0, plus af 0 when --legacy-full-airtime was explicitly selected. It restores every original value only after the exact endpoint is proven. An interrupted run deliberately leaves those transfer guardrails active; rerun the same command with the same work directory to resume and restore them. CPU power saving was not the cause of the observed folder-source failure: that was a USB CDC receive-ring overrun fixed by bounded serial reads. The runtime settings remain conservative isolation and fast-link reliability guardrails.

For every named --relay, the per-step runner also captures rxdelay and txdelay, writes them to relay-timing-settings.json, applies rxdelay 0 and txdelay 0.3 during transfer, verifies the readback, and restores both before the relay leaves TempRadio.

The runner also keeps the watchdog off across the chain, rechecks identity and OTA reach before every package, requires the exact post-boot EndF hash, and only re-enables the watchdog after step 10. Lab success requires endpoint body hash 1FA6AAA3C28D8BD8, target 2FA509C1, hardware RAK_3401, normal radio 910.525 MHz / 62.5 kHz / SF7 / CR5, and the watchdog verified on.

External-radio startup and manual USB recovery

RAK3401 is a distinct target from RAK4631. Its RAK13300/RAK13302 radio is on the WisBlock SPI bus (SPI1 in the Adafruit nRF52 core), with BUSY and DIO1 on P0.09/P0.10. Current builds explicitly enable those NFC-capable pads as GPIO and do not advertise the radio bus as an on-board QSPI flash device.

The RAK13302's FEM and boost supply use switched 3V3_S, but the SX1262 core uses unswitched 3V3. An MCU reset, a 1200-baud DFU touch, or disconnecting only USB may therefore leave a battery-powered SX1262 in its previous state. At startup, current firmware quiesces the FEM, reinitializes the dedicated SPI instance, asserts NRESET, and, if BUSY remains high, sends the Semtech NSS/GET_STATUS wake sequence without waiting on RadioLib first. The one-time board startup cold-starts 3V3_S; later radio retries leave that shared rail enabled so they do not reset GPS or sensor modules.

If BUSY still cannot be released, firmware must not enter a reset loop:

  • Companion starts its USB/BLE management services after three bounded probes, uses nRF52 hardware entropy if it needs a first-boot identity, and retries the radio every 60 seconds. Radio statistics remain zero until recovery.
  • Repeater keeps the same USB CDC session open and prints Radio unavailable; retrying in 60 seconds. It repeats the board-level probe in place. The normal repeater CLI and mesh transport cannot start until the radio responds.

A permanently high BUSY after NRESET and the direct wake transaction indicates an electrically unavailable radio, loose module, or power-domain fault; an MCU reboot cannot manufacture a response. If local access is possible, disconnect the battery as well as USB and reseat the WisBlock module. Remote firmware will continue retrying without requiring that physical intervention or churning the USB device.

For an existing deployed tower using Preview 5, do not change the bootloader as part of this application chain. Its exact local recovery package is the Preview 5 RAK4631 ZIP. For a new RAK3401 installation or local recovery, use the OTAFIX 2.4.3 RAK3401 ZIP from the OTAFIX 2.4.3 release. These are local DFU/SWD recovery assets, not mOTA chain steps.

For a local Serial DFU recovery with the repository helper:

cd /path/to/meshfirmware
MCFIRMWARE_NO_SUDO=1 ./mcfirmware.sh

Select the serial entry whose USB identity is WisCore RAK3401 Board, then choose Custom, nrf52, and the exact RAK3401 .zip. Use flash-update to preserve InternalFS preferences and identity. Use flash-wipe + flash only when erasing application data is intentional. The helper follows the same USB device into bootloader mode, so do not substitute another /dev/ttyACM* merely because its number appears first.