mirror of
https://github.com/vk496/MeshCore.git
synced 2026-09-09 22:35:55 +00:00
ota: relay an external folder of .mota over USB serial (MotaSource)
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#include "MotaSourceSerial.h"
|
||||
#include "MotaSeederProto.h"
|
||||
#include "OtaByteIO.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace mesh {
|
||||
namespace ota {
|
||||
|
||||
bool SerialMotaSource::readByteT(uint8_t& b) {
|
||||
uint32_t t0 = millis();
|
||||
while ((millis() - t0) < _to) {
|
||||
int c = _io.read();
|
||||
if (c >= 0) { b = (uint8_t)c; return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialMotaSource::readExact(uint8_t* b, uint32_t n) {
|
||||
for (uint32_t i = 0; i < n; i++) if (!readByteT(b[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// One request/response transaction. Resync-safe: drains stale input, frames the request with an XOR
|
||||
// checksum, then scans for the response magic and validates op+status+checksum before delivering payload.
|
||||
bool SerialMotaSource::txn(uint8_t op, const uint8_t* args, uint8_t arglen,
|
||||
uint8_t* payload, uint32_t payload_len) {
|
||||
while (_io.read() >= 0) {} // drop any stale/partial bytes before a fresh request
|
||||
uint8_t xs = op;
|
||||
for (uint8_t i = 0; i < arglen; i++) xs ^= args[i];
|
||||
_io.write(MOTA_SEEDER_REQ_MAGIC0); _io.write(MOTA_SEEDER_REQ_MAGIC1);
|
||||
_io.write(op);
|
||||
if (arglen) _io.write(args, arglen);
|
||||
_io.write(xs);
|
||||
_io.flush();
|
||||
|
||||
// scan for response magic 'm' 's' (tolerate leading noise)
|
||||
uint32_t t0 = millis(); bool got = false;
|
||||
uint8_t prev = 0;
|
||||
while ((millis() - t0) < _to) {
|
||||
int c = _io.read();
|
||||
if (c < 0) continue;
|
||||
if (prev == MOTA_SEEDER_RSP_MAGIC0 && (uint8_t)c == MOTA_SEEDER_RSP_MAGIC1) { got = true; break; }
|
||||
prev = (uint8_t)c;
|
||||
}
|
||||
if (!got) return false;
|
||||
|
||||
uint8_t hdr[2];
|
||||
if (!readExact(hdr, 2)) return false; // op, status
|
||||
if (hdr[0] != op) return false;
|
||||
uint8_t rxs = (uint8_t)(MOTA_SEEDER_RSP_MAGIC0 ^ MOTA_SEEDER_RSP_MAGIC1) ^ hdr[0] ^ hdr[1];
|
||||
bool ok = (hdr[1] == MS_STATUS_OK);
|
||||
if (ok && payload_len) {
|
||||
if (!readExact(payload, payload_len)) return false;
|
||||
for (uint32_t i = 0; i < payload_len; i++) rxs ^= payload[i];
|
||||
}
|
||||
uint8_t xsum;
|
||||
if (!readByteT(xsum)) return false;
|
||||
if (xsum != rxs) return false; // corrupt frame -> caller retries
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint8_t SerialMotaSource::count() {
|
||||
uint8_t n = 0;
|
||||
if (!txn(MS_OP_COUNT, nullptr, 0, &n, 1)) return 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
bool SerialMotaSource::describe(uint8_t idx, MotaDesc& out) {
|
||||
uint8_t args[1] = { idx };
|
||||
uint8_t w[MOTA_DESC_WIRE];
|
||||
if (!txn(MS_OP_DESCRIBE, args, 1, w, MOTA_DESC_WIRE)) return false;
|
||||
memcpy(out.mid, w, 4);
|
||||
out.target_id = rd_u32le(w + 4);
|
||||
out.fw_version = rd_u32le(w + 8);
|
||||
out.codec_id = w[12];
|
||||
out.flags = w[13];
|
||||
out.total_size = rd_u32le(w + 14);
|
||||
out.leaves_off = rd_u32le(w + 18);
|
||||
out.block_count = rd_u32le(w + 22);
|
||||
out.payload_off = rd_u32le(w + 26);
|
||||
out.payload_size = rd_u32le(w + 30);
|
||||
// bytes [34,38) reserved (zero) — kept for forward compat without changing MOTA_DESC_WIRE
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialMotaSource::read(uint8_t idx, uint32_t off, uint8_t* buf, uint32_t len) {
|
||||
if (len > 0xFFFF) return false; // single transaction caps at 64 KB (a block is <=1 KB)
|
||||
uint8_t args[7];
|
||||
args[0] = idx;
|
||||
wr_u32le(args + 1, off);
|
||||
args[5] = (uint8_t)(len & 0xFF); args[6] = (uint8_t)(len >> 8);
|
||||
return txn(MS_OP_READ, args, 7, buf, len);
|
||||
}
|
||||
|
||||
} // namespace ota
|
||||
} // namespace mesh
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "OtaSource.h"
|
||||
|
||||
// A MotaSource backed by a host "mota-seeder" daemon over a dedicated Stream (a spare UART / USB-UART).
|
||||
// The device pulls catalog + bytes on demand (MotaSeederProto.h); the folder image is never held on the
|
||||
// device — it streams through. Use a stream that is NOT the text-CLI console so the binary framing never
|
||||
// collides with command/log text. Reads block on the Stream up to `timeout_ms` (OTA is lowest priority,
|
||||
// so a serial round-trip's latency is acceptable; keep the daemon on a fast link).
|
||||
|
||||
namespace mesh {
|
||||
namespace ota {
|
||||
|
||||
class SerialMotaSource : public MotaSource {
|
||||
public:
|
||||
explicit SerialMotaSource(Stream& io, uint32_t timeout_ms = 400) : _io(io), _to(timeout_ms) {}
|
||||
|
||||
uint8_t count() override;
|
||||
bool describe(uint8_t idx, MotaDesc& out) override;
|
||||
bool read(uint8_t idx, uint32_t off, uint8_t* buf, uint32_t len) override;
|
||||
|
||||
private:
|
||||
// Send a request (op+args) and read its response header; on OK, `payload` (if non-null) receives
|
||||
// `payload_len` bytes. Returns true iff a well-formed OK response for `op` arrived in time.
|
||||
bool txn(uint8_t op, const uint8_t* args, uint8_t arglen, uint8_t* payload, uint32_t payload_len);
|
||||
bool readByteT(uint8_t& b); // one byte within the timeout
|
||||
bool readExact(uint8_t* b, uint32_t n);
|
||||
|
||||
Stream& _io;
|
||||
uint32_t _to;
|
||||
};
|
||||
|
||||
} // namespace ota
|
||||
} // namespace mesh
|
||||
Reference in New Issue
Block a user