mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-17 04:04:19 +00:00
deploy: 942df304ca
This commit is contained in:
@@ -2187,14 +2187,16 @@ mode when the hardware can report disconnect. A USB-to-UART bridge may not be
|
||||
able to report this event. A different baud rate, including 57600, does not
|
||||
select ASCII mode.</p>
|
||||
<p>On an ESP32 Full Companion built with <code>OTA_FOLDER_SERIAL</code>, <code>motatool</code> can keep
|
||||
the shared serial console open as an mOTA folder source when WiFi is
|
||||
unavailable:</p>
|
||||
the USB serial port open as an mOTA folder source when WiFi is unavailable:</p>
|
||||
<pre><code class="language-sh">motatool serve --serial /dev/ttyACM0 --dir ./motas -v
|
||||
</code></pre>
|
||||
<p>The tool sends <code>ota folder on</code> through the text CLI, then uses the shared
|
||||
serial mOTA framing. This is separate from the nRF52 exclusive USB ownership
|
||||
mode described below. TCP port 5001 remains the preferred unattended source
|
||||
transport.</p>
|
||||
<p>The tool sends the exact completed <code>ota folder on</code> line when it opens the port.
|
||||
Firmware recognizes it in startup ASCII or idle Binary Companion mode, then
|
||||
transfers the port to exclusive USB mOTA ownership before binary request/reply
|
||||
frames begin. The text terminal and Binary Companion are unavailable on that
|
||||
TTY until <code>motatool</code> detaches the folder. This matches the nRF52 ownership
|
||||
behavior described below. TCP port 5001 remains the preferred unattended
|
||||
source transport.</p>
|
||||
<h2 id="nrf52-usb-mota-mode">nRF52 USB mOTA mode</h2>
|
||||
<p>The nRF52 full target has a third, exclusive USB mode for the host folder.
|
||||
Unmodified <code>motatool serve --serial</code> sends <code>ota folder on</code> when it opens the
|
||||
|
||||
@@ -1671,8 +1671,10 @@ rebooted, its separate interface <code>02</code> continues to provide plaintext
|
||||
and is not a controller or source port. With default logging off, interface
|
||||
<code>02</code> is not enumerated. BLE remains available.</p>
|
||||
<p>ESP32 Full Companion normally uses its dedicated TCP seeder on port 5001.
|
||||
ESP32 builds that also retain serial folder support use the shared-console
|
||||
mOTA framing rather than the nRF52 exclusive USB ownership state.</p>
|
||||
ESP32 builds that also retain serial folder support use the same exclusive USB
|
||||
mOTA ownership state as nRF52. The exact <code>ota folder on</code> line transfers the USB
|
||||
port from startup ASCII or idle Binary Companion mode before binary mOTA frames
|
||||
begin; neither console mode shares that port until the folder detaches.</p>
|
||||
<h2 id="destination-requirements">Destination requirements</h2>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -1798,6 +1800,12 @@ reported bootloader ABI and codec mask against the selected package. If the
|
||||
version command is unavailable on older firmware, the script warns and falls
|
||||
back to the legacy <code>ota self</code> platform marker. If an nRF52 bootloader lacks
|
||||
the required capabilities, install the exact-board OTAFIX bootloader first.
|
||||
Current firmware also reports <code>maxblk:2048</code> near the front of both OTA replies.
|
||||
The runner treats a missing marker as the deployed 1 KiB receive limit, rejects
|
||||
a ready 2 KiB package for such a target before transfer, and passes the live
|
||||
limit to <code>motatool --block-size</code> when it builds from raw firmware. Thus updating
|
||||
an older target from a release ZIP produces a compatible 1 KiB package while a
|
||||
current target retains the 2 KiB application default.
|
||||
<code>ota stats</code> is only an optional EndF version probe. It uses one bounded retry
|
||||
cycle; unsupported firmware or a lost reply falls back directly to the
|
||||
required <code>ver</code> command instead of entering an operator continuation loop.</p>
|
||||
@@ -2162,7 +2170,8 @@ the destination.</p>
|
||||
capabilities, and save the controller's normal radio tuple.</li>
|
||||
<li>Select or build one compatible <strong>v2 application</strong> mOTA and verify all block
|
||||
hashes, Merkle root, full-image hash where applicable, identity fields,
|
||||
signature, codec, base, and the firmware's 1024-byte maximum block size.
|
||||
signature, codec, base, and the live firmware's reported maximum block size
|
||||
(1 KiB for legacy replies without <code>maxblk</code>, currently 2 KiB otherwise).
|
||||
Version-3 bootloader packages are refused before any target state changes.</li>
|
||||
<li>Read every participant's version, save the destination's RXPS state,
|
||||
select the qualified destination policy, and show the confirmation prompt.</li>
|
||||
|
||||
+52
-33
@@ -2100,9 +2100,14 @@ document is the technical/wire specification.</p>
|
||||
<td>usable bytes per packet (<code>src/MeshCore.h</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default block size</td>
|
||||
<td>Default application block size</td>
|
||||
<td><code>2048</code></td>
|
||||
<td><code>block_size_log2 = 0x0B</code>; deployed 1 KiB application packages remain accepted</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bootloader-package block size</td>
|
||||
<td><code>1024</code></td>
|
||||
<td><code>block_size_log2 = 0x0A</code></td>
|
||||
<td>fixed <code>block_size_log2 = 0x0A</code> for format 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OTA discovery TX priority</td>
|
||||
@@ -2116,6 +2121,12 @@ document is the technical/wire specification.</p>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Current application firmware publishes its receive/reassembly ceiling as the compact <code>maxblk:2048</code> field
|
||||
near the front of both <code>ota status</code> and <code>ota self</code>. Keeping the field ahead of variable diagnostics ensures
|
||||
it survives the 160-byte remote-admin reply limit. Host updaters must treat an absent field as the deployed
|
||||
legacy capability (<code>1024</code>), reject a ready package whose manifest block size exceeds that capability, and use
|
||||
the reported size when turning a raw firmware image into a new application package. This capability does not
|
||||
change the format-3 bootloader package's fixed 1 KiB geometry.</p>
|
||||
<hr />
|
||||
<h2 id="2-firmware-image-the-endf-trailer">2. Firmware image & the <code>EndF</code> trailer</h2>
|
||||
<p>Every OTA-capable build appends a fixed <strong>56-byte</strong> <code>EndF</code> trailer to its flashed image so a running node
|
||||
@@ -2256,7 +2267,7 @@ Only <code>leaves[]</code> is variable (one 4-byte hash per block). So the manif
|
||||
7 4 fw_version MAJOR<<24 | MINOR<<16 | PATCH<<8 | pre (comparable uint32)
|
||||
11 4 image_size size of the reconstructed image (BODY||EndF)
|
||||
15 4 payload_size PAYLOAD bytes in this container
|
||||
19 1 block_size_log2 e.g. 0x0A = 1024
|
||||
19 1 block_size_log2 e.g. 0x0B = 2048 (new application default); 0x0A = deployed 1024
|
||||
20 4 merkle_root sha2-256:4 over PAYLOAD blocks (Section 6) - also the manifest_id
|
||||
24 32 image_hash sha2-256:32 of the reconstructed image - SECURITY anchor
|
||||
56 1 codec_id 0=full/raw, 1=detools-sequential, 2=detools-in-place
|
||||
@@ -2370,8 +2381,9 @@ deltas use <code>--compression crle</code>. <strong>Do not reimplement the codec
|
||||
<p>Verifies each PAYLOAD block against the signed <code>merkle_root</code> <strong>before</strong> the whole payload exists, so
|
||||
corruption/forgery is localized to a block. Implemented in <code>MerkleTree.cpp</code>.</p>
|
||||
<ul>
|
||||
<li><strong>Blocks:</strong> PAYLOAD splits into <code>BC = ceil(payload_size / B)</code> blocks, <code>B = 2^block_size_log2</code> (default
|
||||
1024). The last block is its real length (<strong>no zero padding</strong>).</li>
|
||||
<li><strong>Blocks:</strong> PAYLOAD splits into <code>BC = ceil(payload_size / B)</code> blocks, <code>B = 2^block_size_log2</code> (new
|
||||
application default 2048; deployed 1024-byte application packages remain valid). The last block is its
|
||||
real length (<strong>no zero padding</strong>). Format-3 bootloader packages remain fixed at 1024 bytes.</li>
|
||||
<li><strong>Leaf:</strong> <code>leaves[i] = sha2-256:4( block_i_bytes )</code>.</li>
|
||||
<li><strong>Internal node:</strong> <code>node = sha2-256:4( left || right )</code> (4+4 input bytes).</li>
|
||||
<li><strong>Odd level:</strong> an odd count promotes the <strong>last node unchanged</strong> to the next level (no duplication).</li>
|
||||
@@ -2595,12 +2607,17 @@ label and stage a different manifest.</p>
|
||||
OTA_MANIFEST: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up to OTA_MF_FRAG=176 B/frag
|
||||
OTA_REQ: manifest_id[4] { block_idx(uint16) want_mask(uint16) }[1..4]
|
||||
# legacy: bit k requests its 160-byte fragment
|
||||
# v2: bit15=marker, bit14=allow transport DEFLATE, bits0..12=fragment bitmap
|
||||
# v2: bit15=marker, bit14=allow transport DEFLATE,
|
||||
# bit13=2 KiB descriptor, bits0..12=fragment bitmap;
|
||||
# bit12 must be zero when bit13 is set (0xFFFF stays legacy)
|
||||
OTA_DATA legacy: manifest_id[4] block_idx(uint16) frag_off(uint16) data[<=160]
|
||||
OTA_DATA v2: manifest_id[4] block_idx(uint16) descriptor(uint16)
|
||||
stream_id[4] data[<=171]
|
||||
# descriptor: bit15=marker, bit14=DEFLATE, bits13..10=fragment,
|
||||
# bits9..0=complete encoded length minus one
|
||||
# deployed 1 KiB descriptor: bit15=marker, bit14=DEFLATE,
|
||||
# bits13..10=fragment, bits9..0=complete encoded length minus one
|
||||
# negotiated 2 KiB descriptor: bit15=marker, bits14..11=fragment,
|
||||
# bits10..0=complete encoded length minus one; DEFLATE iff that
|
||||
# length is strictly less than the manifest-derived raw block length
|
||||
OTA_REQ_PROOF: manifest_id[4] block_idx(uint16)
|
||||
OTA_PROOF: manifest_id[4] block_idx(uint16) n_proof(1) proof[] # n_proof x 4 bytes
|
||||
OTA_GET_LEAVES: manifest_id[4] want_mask(uint16) # bit k = send leaves fragment k; 0xFFFF = all
|
||||
@@ -2623,22 +2640,25 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Negotiated 171-byte fragments:</strong> the deployed profile remains unchanged: <code>frag_off</code> is a byte offset and
|
||||
<code>data[]</code> carries at most 160 bytes. A new fetcher marks an <code>OTA_REQ</code> row as v2 and includes all seven legacy
|
||||
bits on its first request. Old sources ignore/mask the high flags and can therefore return a complete legacy
|
||||
block immediately; receipt of an untagged <code>OTA_DATA</code> switches that fetch session to the old geometry. If no
|
||||
v2 data appears by the first adaptive deadline, the fetcher retries with an ordinary legacy mask. New
|
||||
sources answer v2 with a packed descriptor, a repeated 4-byte representation ID, and exactly 171 data bytes
|
||||
except the final fragment. A raw 1 KiB block therefore falls from seven data packets to six. Message type
|
||||
IDs do not change, so multi-hop relays continue to forward request/data/proof packets opaquely with the same
|
||||
priority.</p>
|
||||
<code>data[]</code> carries at most 160 bytes. A new fetcher marks an <code>OTA_REQ</code> row as v2. For a deployed 1 KiB block,
|
||||
its first request also includes every fragment bit needed by the legacy representation, so an old source can
|
||||
answer it completely. The extended 2 KiB profile reserves bit 12 as zero; this both fits its twelve v2
|
||||
fragments and ensures the deployed <code>0xFFFF</code> legacy all-fragments request is never misclassified. Receipt of
|
||||
untagged <code>OTA_DATA</code> switches the fetch session to the old geometry. If no v2 data appears by the first
|
||||
adaptive deadline, the fetcher retries with an ordinary legacy mask. New sources answer v2 with a packed descriptor, a repeated 4-byte representation
|
||||
ID, and exactly 171 data bytes except the final fragment. A raw 1 KiB block falls from seven data packets to
|
||||
six; a raw 2 KiB block falls from thirteen to twelve. Message type IDs do not change, so multi-hop relays
|
||||
continue to forward request/data/proof packets opaquely with the same priority.</p>
|
||||
</li>
|
||||
<li><strong>Transport-only DEFLATE:</strong> a fetcher sets the v2 DEFLATE-permission bit only when its application includes
|
||||
and configures the decoder. A source may encode each logical block as an independent raw RFC 1951 stream
|
||||
with full stored, fixed-Huffman, and dynamic-Huffman support (<code>BTYPE=0/1/2</code>). Compression is used only when
|
||||
strictly smaller, otherwise the source returns raw v2 data. The
|
||||
receiver inflates to the manifest-derived block length, then performs the unchanged Merkle proof and writes
|
||||
the original bytes to staging. Thus existing <code>.mota</code> containers, signatures, resume markers, and legacy
|
||||
bootloader apply remain unchanged. The 4-byte <code>stream_id</code> is SHA-256:4 of the complete raw or compressed
|
||||
the original bytes to staging. Thus staging and bootloader apply remain uncompressed. Deployed 1 KiB
|
||||
<code>.mota</code> containers remain compatible; new 2 KiB containers require a receiver advertising the extended
|
||||
descriptor. Format-3 bootloader packages retain their strict 1 KiB geometry. The 4-byte <code>stream_id</code> is
|
||||
SHA-256:4 of the complete raw or compressed
|
||||
representation and appears in every fragment. A receiver locks <code>{encoding, encoded length, stream_id}</code> for
|
||||
a block, preventing fragments from independently encoded seeders from being mixed. After one sparse retry,
|
||||
a second stalled interval clears the full in-flight window and switches the session to legacy geometry, so
|
||||
@@ -2647,12 +2667,11 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
bounds output to the exact logical block length and rejects truncation, malformed streams, and trailing
|
||||
whole bytes. Builds without OTA do not link the decoder. This application capability is independent of the
|
||||
bootloader because transport data is inflated before the unchanged staged container is written.</li>
|
||||
<li><strong>Adaptive flight size is not signed block size.</strong> The container continues to use 1 KiB Merkle leaves and
|
||||
each slot is one existing 1 KiB block. A clean link changes how many of those blocks one <code>OTA_REQ</code> names:
|
||||
1, then 2, then 3, then the compiled cap. The manifest stores <code>block_size_log2</code>, so 3 KiB is not a valid
|
||||
logical geometry; enabling 2 KiB would require larger device reassembly buffers and new-package/bootloader
|
||||
validation while saving only one request/proof pair per 2 KiB. It does not address premature retries, which
|
||||
were the dominant packet multiplier.</li>
|
||||
<li><strong>Adaptive flight size is not signed block size.</strong> Each slot holds one manifest-defined block: 1 KiB for
|
||||
deployed application containers or 2 KiB for new ones. A clean link changes how many complete blocks one
|
||||
<code>OTA_REQ</code> names: 1, then 2, then 3, then the compiled cap. It never changes block geometry. Two KiB halves
|
||||
the leaf, block-request, and proof count for a payload while giving each independent DEFLATE stream a larger
|
||||
history window.</li>
|
||||
<li><strong>Append-only request-window compatibility:</strong> the first four-byte request row is exactly the original
|
||||
<code>block_idx + want_mask</code> body. Old sources decode that row and ignore appended bytes. New sources queue all
|
||||
rows. If a new fetcher meets an old source, the unserved tail rows eventually time out and are recovered as
|
||||
@@ -2667,8 +2686,8 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
half-duplex radios: re-requesting a whole multi-fragment burst let the periodic retry (a transmit) collide
|
||||
with the tail of the in-flight burst and drop the same fragment forever - a hang. Requesting only the hole
|
||||
removes the burst, so there is nothing to collide with. The block/manifest mask matches the 16-bit
|
||||
reassembly bitmap (<=16 fragments/block; legacy 1 KB blocks = 7, raw v2 = 6). <code>OTA_PROOF</code> is a single packet
|
||||
and needs no mask.</li>
|
||||
reassembly bitmap (<=16 fragments/block; 1 KiB is 7 legacy or 6 raw-v2 fragments; 2 KiB is 13 legacy or 12
|
||||
raw-v2 fragments). <code>OTA_PROOF</code> is a single packet and needs no mask.</li>
|
||||
<li><strong>Data and proof remain separate packets, without a normal extra round trip.</strong> A server retains requested
|
||||
blocks in a bounded descriptor queue, admits at most one response per main-loop pass, and sends one
|
||||
<code>OTA_PROOF</code> after each block's requested fragments. Before admitting that proactive proof, the source leaves
|
||||
@@ -2693,12 +2712,12 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
<tr>
|
||||
<td><code>OTA_DATA</code> legacy</td>
|
||||
<td>9 B (type+mid4+idx2+off2)</td>
|
||||
<td><code>OTA_FRAG_DATA = 160</code> -> 7 frags per 1 KB block</td>
|
||||
<td><code>OTA_FRAG_DATA = 160</code> -> 7 raw frags/1 KiB or 13/2 KiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>OTA_DATA</code> v2</td>
|
||||
<td>13 B (legacy header + stream ID)</td>
|
||||
<td><code>OTA_FRAG_DATA_V2 = 171</code> -> 6 raw frags per 1 KB block; fewer when compressed</td>
|
||||
<td><code>OTA_FRAG_DATA_V2 = 171</code> -> 6 raw frags/1 KiB or 12/2 KiB; fewer when compressed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>OTA_MANIFEST</code></td>
|
||||
@@ -2717,8 +2736,8 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>A served mota supports up to <code>OTA_MAX_BLOCK/4</code> leaves in the default 4 KB proof scratch (<=1024 blocks ~ 1 MB
|
||||
payload); larger self-images pass a bigger scratch buffer.</p>
|
||||
<p>A served mota supports up to 1024 leaves in the default 4 KiB proof scratch (about 2 MiB of payload at the
|
||||
new 2 KiB default, or 1 MiB for a deployed 1 KiB container); larger self-images pass a bigger scratch buffer.</p>
|
||||
<h3 id="86-temporary-radio-and-transfer-boundary">8.6 Temporary-radio and transfer boundary</h3>
|
||||
<p>OTA packets may cross normal mesh relay hops, but each participating node processes or relays them only while
|
||||
its <code>tempradio</code> window is actually running. A receiver selects missing blocks in serial order into a bounded
|
||||
@@ -2821,7 +2840,7 @@ class MotaSource {
|
||||
uint8_t* buf, uint16_t cap, uint16_t* len); // optional raw RFC 1951
|
||||
};
|
||||
</code></pre>
|
||||
<p>To serve an external mota the node reads its manifest-minus-leaves + <code>leaves[]</code> into RAM (<=4 KB for <=1024
|
||||
<p>To serve an external mota the node reads its manifest-minus-leaves + <code>leaves[]</code> into RAM (<=4 KiB for <=1024
|
||||
blocks) and streams payload blocks from the source on demand; proofs are generated from the read leaves.</p>
|
||||
<h3 id="102-the-mota-seeder-transport-motaseederprotoh">10.2 The <code>mota-seeder</code> transport (<code>MotaSeederProto.h</code>)</h3>
|
||||
<p>A <code>MotaSource</code> is fed by a host that serves a folder over the device's <strong>USB serial</strong> (the same console the
|
||||
@@ -2846,8 +2865,8 @@ MotaDesc wire (38 B): mid[4] target_id(4) fw_version(4) codec(1) flags(1)
|
||||
status: 0 = OK, non-zero = error (out of range / past EOF).
|
||||
</code></pre>
|
||||
<p><code>SerialMotaSource</code> splits logical reads into replies of at most 192 payload
|
||||
bytes. A manifest's leaf table can exceed 256 bytes and payload blocks are
|
||||
normally 1 KiB; requesting either in one transaction can overrun common USB
|
||||
bytes. A manifest's leaf table can exceed 256 bytes and new payload blocks are
|
||||
normally 2 KiB; requesting either in one transaction can overrun common USB
|
||||
CDC/UART receive rings even though the host successfully wrote the complete
|
||||
reply. Chunking is internal to the transport and does not change <code>OP_READ</code> or
|
||||
the <code>MotaSource</code> random-access contract.</p>
|
||||
|
||||
@@ -1887,12 +1887,12 @@ re-running a <code>validate</code> pull re-begins fresh (it never resumes a stal
|
||||
trusted - every kept block is checked against the target's own fingerprints, so a mismatched or missing seed
|
||||
just means those blocks are fetched over the radio (correct result, only slower).</p>
|
||||
<p>The node fetches from one source as <strong>primary traffic</strong>, with bounded adaptive request flights. Every session
|
||||
probes with one 1 KiB block, then clean flights grow <code>1 -> 2 -> 3 -> 4</code> blocks on RAK3401 OTA builds. All blocks
|
||||
probes with one manifest block, then clean flights grow <code>1 -> 2 -> 3 -> 4</code> blocks on RAK3401 OTA builds. New
|
||||
application packages use 2 KiB blocks; deployed 1 KiB packages remain supported. All blocks
|
||||
in a flight share one request packet, and the receiver stays silent until the source/relays finish returning
|
||||
them. A recovery halves the next flight. Retry timing follows the active SF/BW airtime, duty budget, and path
|
||||
length, so faster settings recover sooner without a fixed one-second request colliding with a valid multi-hop
|
||||
response. The signed block size itself remains 1 KiB. Mesh repeaters carry the packets only while their
|
||||
temporary-radio windows are active.
|
||||
response. Mesh repeaters carry the packets only while their temporary-radio windows are active.
|
||||
Check progress with <code>ota status</code>.</p>
|
||||
<p>If a <code>folder</code> pull loses its link mid-transfer, <code>ota status</code> shows <strong>paused</strong> - the host keeps the
|
||||
partial and the pull resumes (filling only what's missing) the moment you reconnect motatool; it never
|
||||
|
||||
+108
-46
@@ -1184,10 +1184,10 @@
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#why-preview-5-and-the-old-staging-ceiling-work" class="md-nav__link">
|
||||
<a href="#why-the-deployed-otafix24-and-old-staging-ceiling-work" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Why Preview 5 and the old staging ceiling work
|
||||
Why the deployed OTAFIX2.4 and old staging ceiling work
|
||||
|
||||
</span>
|
||||
</a>
|
||||
@@ -1225,6 +1225,17 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#superseded-b40d2e6c-prerelease" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Superseded b40d2e6c prerelease
|
||||
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -1504,10 +1515,10 @@
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#why-preview-5-and-the-old-staging-ceiling-work" class="md-nav__link">
|
||||
<a href="#why-the-deployed-otafix24-and-old-staging-ceiling-work" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Why Preview 5 and the old staging ceiling work
|
||||
Why the deployed OTAFIX2.4 and old staging ceiling work
|
||||
|
||||
</span>
|
||||
</a>
|
||||
@@ -1545,6 +1556,17 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#superseded-b40d2e6c-prerelease" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Superseded b40d2e6c prerelease
|
||||
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -1710,23 +1732,23 @@
|
||||
<p>Status: <strong>published prerelease for controlled, recoverable lab use.</strong> 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
|
||||
<code>fd98bc90</code> chain. The replacement step 10 installs the current <code>b40d2e6c</code>
|
||||
<code>fd98bc90</code> chain. The replacement step 10 installs the current <code>3f6eddd5</code>
|
||||
endpoint. That replacement package and the hardened current runner have
|
||||
passed offline reconstruction plus both required bootloader simulators, but
|
||||
passed offline reconstruction plus the exact deployed bootloader simulator, 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.</p>
|
||||
</blockquote>
|
||||
<p>The prerelease is
|
||||
<a href="https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c"><code>rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c</code></a>.
|
||||
<a href="https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.6-3f6eddd5"><code>rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.6-3f6eddd5</code></a>.
|
||||
Use this asset:</p>
|
||||
<pre><code class="language-text">RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
|
||||
<pre><code class="language-text">RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li>prerelease ZIP SHA-256: <code>8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4</code></li>
|
||||
<li>inner <code>SHA256SUMS.txt</code> SHA-256: <code>29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a</code></li>
|
||||
<li>endpoint image SHA-256: <code>a7761da8af1f2447d7f01d72e2b1456b99c0e874a165d6483f48ab1f58e8d905</code></li>
|
||||
<li>endpoint EndF body hash: <code>1FA6AAA3C28D8BD8</code></li>
|
||||
<li>prerelease ZIP SHA-256: <code>135783dd8777490db2422a7543f7d80e3cf03f621d5ac362da930abbc1850dc4</code></li>
|
||||
<li>inner <code>SHA256SUMS.txt</code> SHA-256: <code>93835ed497c579be0a2322292e7f15ab095e7c8ab7892833fe7df3130dbdaba4</code></li>
|
||||
<li>endpoint image SHA-256: <code>cd1e9b819f9e918f9f2ceaf37b8a74bf242e2be78f319af2cf02a42d7729d0f4</code></li>
|
||||
<li>endpoint EndF body hash: <code>9BD7CF682EE065AE</code></li>
|
||||
</ul>
|
||||
<p><code>--verify-only</code> remains the safe default. Live preflight and installation of
|
||||
this prerelease require <code>--accept-test-candidate</code>. That switch bypasses only
|
||||
@@ -1740,11 +1762,19 @@ target:</p>
|
||||
<li>hardware: <code>RAK_3401</code></li>
|
||||
<li>role: <code>RAK_3401_repeater_lora_ota_no_external_sensors</code></li>
|
||||
<li>start: <code>v1.16.7.0-c1caa5ad</code>, EndF <code>71F4026CBE4B8B74</code></li>
|
||||
<li>endpoint source: MeshCore <code>b40d2e6cb2a185db740175e8b161ea71736d19d9</code></li>
|
||||
<li>endpoint label: <code>v1.17.1.5-halo-keymind-cascade-dev-b40d2e6c</code></li>
|
||||
<li>endpoint packed version: <code>0x01110105</code></li>
|
||||
<li>endpoint source: MeshCore <code>3f6eddd5c20f1fa3df6ba1cc208efbe3e2fffb43</code></li>
|
||||
<li>endpoint label: <code>v1.17.1.6-halo-keymind-cascade-dev-3f6eddd5</code></li>
|
||||
<li>endpoint packed version: <code>0x01110106</code></li>
|
||||
<li>deployment target key: <code>63d8df6387eaffd2e25db7d2a8ad967a65202182a48d681d7e7a9260f917280d</code></li>
|
||||
</ul>
|
||||
<p>The exact starting application is the
|
||||
<a href="https://github.com/mikecarper/MeshCore/releases/tag/lora-ota-v1.16.07-halo-keymind-cascade-dev-3a6af3bf"><code>lora-ota-v1.16.07-halo-keymind-cascade-dev-3a6af3bf</code></a>
|
||||
RAK3401 asset. Its outer ZIP SHA-256 is
|
||||
<code>58ead2ea18efafda186bc9d983fe08cdf65f69b0e5a3a1c72c18fe35948a254c</code>;
|
||||
its 566,912-byte <code>firmware.bin</code> SHA-256 is
|
||||
<code>4301fc63ebd661c70f9bc40e7eca44d3cb8a358cb3c0075e700d9676a57478cb</code>.
|
||||
The endpoint build ZIP SHA-256 is
|
||||
<code>a9fca9be145a96feb650b57c43b5ed173d74d2f3194da12e31b1c54352635b56</code>.</p>
|
||||
<h3 id="retained-and-omitted-hardware-support">Retained and omitted hardware support</h3>
|
||||
<p>Despite the legacy <code>no_external_sensors</code> target name, the endpoint retains the
|
||||
RAK12501 GPS provider and the common INA3221, INA219, INA226, and INA260 I2C
|
||||
@@ -1761,7 +1791,7 @@ would discard desired voltage/current monitoring and create another firmware
|
||||
identity without helping this route.</p>
|
||||
<p>Do not use the chain on another target ID, hardware family, starting image, or
|
||||
firmware body hash. The runner checks all four.</p>
|
||||
<h2 id="why-preview-5-and-the-old-staging-ceiling-work">Why Preview 5 and the old staging ceiling work</h2>
|
||||
<h2 id="why-the-deployed-otafix24-and-old-staging-ceiling-work">Why the deployed OTAFIX2.4 and old staging ceiling work</h2>
|
||||
<p>The deployed bootloader is not changed and no package uses the newer expanded
|
||||
<code>0xED000</code> ceiling. Each package remains bottom-aligned below the old
|
||||
bootloader's <code>0xD4000</code> scan ceiling. Its detools workspace is page-aligned and
|
||||
@@ -1779,18 +1809,28 @@ workspace.</p>
|
||||
<p>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 <code>0xFCDEA</code> even though it is two
|
||||
bytes off a word boundary. The <code>b40d2e6c</code> 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.
|
||||
<code>0.9.2-OTAFIX2.4</code> marker at absolute address <code>0xFCD16</code>. That immutable marker
|
||||
advertises ABI 2, codec mask <code>0x0004</code>, and internal storage profile 0. The
|
||||
<code>3f6eddd5</code> endpoint retains the matching legacy application-update fallback.
|
||||
Its separately compiled internal bootloader-update feature remains unavailable
|
||||
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.</p>
|
||||
<p>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
|
||||
<p>The exact installed bootloader is the
|
||||
<a href="https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/download/0.9.2-OTAFIX2.4/wiscore_rak4631_board_bootloader-0.9.2-OTAFIX2.4_s140_6.1.1.zip"><code>0.9.2-OTAFIX2.4</code> RAK4631 asset</a>.
|
||||
Its outer ZIP SHA-256 is
|
||||
<code>5e29e7a8982cc2ed5f8868435556eb09387250fe5b9a7e8443bac325247dfed1</code>;
|
||||
its 191,040-byte <code>sd_bl.bin</code> SHA-256 is
|
||||
<code>a97f834388db0f1da6c29f6d8f458ff6d3e6a26034709044566384838919bb23</code>.
|
||||
The exact simulator was compiled from release commit
|
||||
<code>d73de8372e89b8ef352747c8bc7a1aaeab80fbfe</code>; its executable SHA-256 is
|
||||
<code>9dacf24b1023fe2f4c620419417649ccc9538dd4a611b47bc19b7fedb97ceba6</code>.</p>
|
||||
<p>The schema-2 exhaustive search checked 1,799 candidate geometries and found 12
|
||||
feasible forward edges in the declared exact 12-node inventory. It found one
|
||||
shortest ten-package route and selected the smallest total container bytes as
|
||||
the secondary objective. This minimum applies only to that declared inventory
|
||||
and geometry table, not every conceivable new bridge image. The selected route
|
||||
is:</p>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -1879,31 +1919,38 @@ is:</p>
|
||||
<tr>
|
||||
<td style="text-align: right;">10</td>
|
||||
<td>1.17.1.3</td>
|
||||
<td>1.17.1.5</td>
|
||||
<td style="text-align: right;"><code>0x74000</code></td>
|
||||
<td style="text-align: right;">174,744</td>
|
||||
<td style="text-align: right;">61,440</td>
|
||||
<td>1.17.1.6</td>
|
||||
<td style="text-align: right;"><code>0x76000</code></td>
|
||||
<td style="text-align: right;">197,825</td>
|
||||
<td style="text-align: right;">28,672</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Total mOTA transfer data is 1,384,252 bytes. <code>ROUTE.json</code>, <code>CHAIN.csv</code>, and
|
||||
<p>Total mOTA transfer data is 1,407,333 bytes. <code>ROUTE.json</code>, <code>CHAIN.csv</code>, and
|
||||
<code>validation-results.json</code> in the bundle pin the exact geometry and image hash
|
||||
for every transition.</p>
|
||||
<p>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
|
||||
<code>FE65A6135A1E7B3F</code> v1.17.1.3 body to <code>1FA6AAA3C28D8BD8</code>, has offline
|
||||
<code>FE65A6135A1E7B3F</code> v1.17.1.3 body to <code>9BD7CF682EE065AE</code>, 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.</p>
|
||||
zero-filled and erased-workspace reconstruction, and the exact deployed
|
||||
<code>0.9.2-OTAFIX2.4</code> 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.</p>
|
||||
<p>The 171-byte transport DEFLATE support lands only in the final <code>3f6eddd5</code>
|
||||
application. The starting receiver and every receiver that accepts steps 1-10
|
||||
lack that inflate path, so all ten legacy-chain transfers remain raw and the
|
||||
1,407,333-byte total above is not automatically reduced over LoRa. A later
|
||||
update can negotiate transport compression after this endpoint is running;
|
||||
the application inflates before staging, and the immutable bootloader still
|
||||
applies an ordinary ABI-2/codec-2 container.</p>
|
||||
<h2 id="host-requirements">Host requirements</h2>
|
||||
<p>Install Python 3.9 or newer, <code>meshcli</code> 1.6.0 or newer, the current <code>motatool</code>,
|
||||
and an OTA-enabled Full Companion or repeater that can seed mOTA files.</p>
|
||||
<h2 id="verify-offline">Verify offline</h2>
|
||||
<p>No password or device is needed:</p>
|
||||
<pre><code class="language-bash">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 \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip \
|
||||
--work-dir ./rak3401-mota-chain-work \
|
||||
--motatool /path/to/motatool \
|
||||
--verify-only
|
||||
@@ -1926,7 +1973,7 @@ watchdog, reachability, and post-boot gates still run.</p>
|
||||
<pre><code class="language-bash">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 \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip \
|
||||
--work-dir ./rak3401-mota-chain-work \
|
||||
--controller-serial /dev/ttyACM0 \
|
||||
--source-tcp 192.168.1.51:5001 \
|
||||
@@ -1946,6 +1993,21 @@ EndF body hash matches an exact chain node. Never manually skip a package.
|
||||
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.</p>
|
||||
<h2 id="superseded-b40d2e6c-prerelease">Superseded <code>b40d2e6c</code> prerelease</h2>
|
||||
<p>The immediately preceding prerelease remains pinned for offline provenance:</p>
|
||||
<ul>
|
||||
<li>release: <a href="https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c"><code>rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c</code></a></li>
|
||||
<li>asset: <code>RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip</code></li>
|
||||
<li>outer ZIP SHA-256: <code>8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4</code></li>
|
||||
<li>inner <code>SHA256SUMS.txt</code> SHA-256: <code>29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a</code></li>
|
||||
<li>endpoint image SHA-256: <code>a7761da8af1f2447d7f01d72e2b1456b99c0e874a165d6483f48ab1f58e8d905</code></li>
|
||||
<li>endpoint EndF body hash: <code>1FA6AAA3C28D8BD8</code></li>
|
||||
</ul>
|
||||
<p>Its first nine packages are byte-for-byte identical to the current release and
|
||||
the physical <code>fd98bc90</code> evidence. Its different step 10 had offline
|
||||
qualification only. The runner recognizes this exact archive separately and
|
||||
keeps live use gated; it is not interchangeable with the current <code>3f6eddd5</code>
|
||||
asset.</p>
|
||||
<h2 id="legacy-fd98bc90-physical-and-swd-qualification">Legacy <code>fd98bc90</code> physical and SWD qualification</h2>
|
||||
<p>The earlier local candidate remains the exact physical evidence source for
|
||||
this route:</p>
|
||||
@@ -1962,9 +2024,9 @@ deployed <code>0.9.2-OTAFIX2.4</code> bootloader and a Heltec V4 source at 909.9
|
||||
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 <code>b40d2e6c</code> prerelease. The two candidates have different step
|
||||
10 packages and endpoints, so the physical result and step-10 timing must not
|
||||
be attributed to <code>b40d2e6c</code>.</p>
|
||||
packages in the <code>b40d2e6c</code> and <code>3f6eddd5</code> prereleases. All three candidates
|
||||
have different step 10 packages or endpoints, so the physical result and
|
||||
step-10 timing must not be attributed to either later prerelease.</p>
|
||||
<h3 id="measured-direct-link-timing-for-fd98bc90">Measured direct-link timing for <code>fd98bc90</code></h3>
|
||||
<p>The first <code>ready to install</code> transfer ages were:</p>
|
||||
<table>
|
||||
@@ -2109,16 +2171,16 @@ 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 <code>ota ls</code>.</p>
|
||||
<h3 id="1-verify-and-extract-the-asset">1. Verify and extract the asset</h3>
|
||||
<pre><code class="language-bash">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
|
||||
<pre><code class="language-bash">sha256sum RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
unzip RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
cd RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5
|
||||
sha256sum -c SHA256SUMS.txt
|
||||
for package in motas/*.mota; do motatool verify "$package" || exit 1; done
|
||||
</code></pre>
|
||||
<p>The outer hash must be
|
||||
<code>8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4</code>,
|
||||
<code>135783dd8777490db2422a7543f7d80e3cf03f621d5ac362da930abbc1850dc4</code>,
|
||||
and the SHA-256 of the extracted <code>SHA256SUMS.txt</code> must be
|
||||
<code>29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a</code>.
|
||||
<code>93835ed497c579be0a2322292e7f15ab095e7c8ab7892833fe7df3130dbdaba4</code>.
|
||||
Do not continue after any checksum or verification error.</p>
|
||||
<h3 id="2-record-and-prepare-the-destination">2. Record and prepare the destination</h3>
|
||||
<p>Before changing anything, save the complete replies from:</p>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user