Below is a ready‑to‑use HTML document that you can save as `convoy_mode.html` and open in any browser. It contains the full “convoy‑mode” design, mesh‑network hardware & cost estimates, software architecture, protocols and example code. Feel free to edit the styles or content to match the rest of your site. ```html Seastead Convoy Mode – Design & Network Overview

Seastead Convoy Mode – Design & Network Overview

This document expands the “convoy mode” concept for a fleet of tri‑float seasteads. It includes the logical grid layout, a recommended low‑cost Wi‑Fi mesh network, the software stack, protocol definitions, joining logic, safety considerations, and a sample implementation.

Scope: All seasteads run the same open‑source autopilot software, have Starlink for Internet, a dedicated on‑board GPS/RTK unit, and can share local data via a mesh network.

2. Grid & Spatial Layout

3. Communications Hardware

3.1 Recommended Hardware

ComponentTypical ModelKey SpecsApprox. Cost (USD)
Wi‑Fi Radio (5 GHz) Ubiquiti NanoStation 5AC Loco (or MikroTik SXT 5) 802.11ac, 2×2 MIMO, 23 dBi gain, 10‑20 km range (LOS), 450 Mbps max throughput $130 – $150
Dual‑Band AP (optional redundancy) TP‑Link EAP225 (ceiling AP) 2.4/5 GHz, 802.11ac, 5 dBi, 300 Mbps per band $80
Antenna (directional, 5 GHz) MikroTik mANT 5‑19 (19 dBi) Beamwidth ~30°, 30 km LOS with clear line‑of‑sight $40
POE Injector + cabling Ubiquiti 48 V 0.5 A PoE injector Power over Ethernet for radio $15 – $20
Mounting hardware Stainless steel pole & clamps (2 m above deck) Marine‑grade, anti‑corrosion $30 – $50
Network switch (optional, for multi‑radio) TP‑Link TL‑SG108E (8‑port, managed) 1 Gbps, VLAN support $45

3.2 Range & Data Rate

3.3 Cost Estimates (per seastead)

ItemQtyUnit PriceTotal (USD)
5 GHz Wi‑Fi radio (NanoStation AC)1$140$140
Directional antenna (mANT 5‑19)1$40$40
POE injector + cables1$20$20
Mount pole & hardware1$40$40
Network switch (8‑port, optional)1$45$45
Total~$285
Tip: If you already have a 2.4 GHz mesh for legacy devices, keep it separate. Use the 5 GHz mesh for the convoy’s real‑time control and vision traffic.

4. Mesh Network Software

4.1 OS & Firmware

Run a lightweight Linux distribution (e.g., OpenWrt 21.02 or Debian minimal) on a small industrial PC (e.g., BeagleBone Black, Raspberry Pi 4, or a fanless x86 board). This gives you full control over the Wi‑Fi driver, hostapd, and routing daemons.

4.2 Routing Protocol

4.3 Security

5. Data Structures & Protocols

5.1 Heartbeat / Watch Status

Every node publishes a Heartbeat message at 2 Hz. The payload is JSON:

{
  "type": "heartbeat",
  "nodeId": "SEASTEAD‑42",
  "timestamp": "2026-01-25T14:32:01.123Z",
  "position": { "x": 567.23, "y": 1024.89, "z": 0.0 },   // meters (RTK)
  "heading": 45.6,          // degrees true
  "speed": 0.2,             // m/s
  "watchStatus": { "onWatch": true, "confirmBy": "operator1", "confirmTs": "..." },
  "health": { "cpuTemp": 58.3, "batteryVoltage": 48.2 }
}

5.2 Grid‑Assignment Messages

{
  "type": "gridAssign",
  "assignedNode": "SEASTEAD‑42",
  "targetPos": { "x": 580.0, "y": 1020.0 },  // assigned grid coordinate
  "assignedBy": "SEASTEAD‑01",
  "timestamp": "2026-01-25T14:32:05.000Z",
  "ttl": 300   // seconds until the assignment expires
}

5.3 Object‑Tracking Database

Tracked objects are stored in a distributed CRDT‑based hash table (e.g., using libp2p’s Kademlia + a Grow‑only Set). Each entry:

{
  "id": "OBJ‑00123",
  "firstSeen": "2026-01-25T14:30:00Z",
  "lastSeen": "2026-01-25T14:32:20Z",
  "class": "ship",
  "position": { "lat": 21.2345, "lon": -158.9876 },
  "velocity": { "vEast": 2.5, "vNorth": 0.8 }, // m/s
  "confidence": 0.93,
  "observedBy": ["SEASTEAD‑01","SEASTEAD‑05"],
  "observations": [ /* raw camera parallax data */ ]
}

All nodes append observations; the CRDT automatically merges concurrent updates.

6. Convoy‑Join Procedure (State Machine)

  1. Idle → Approach: The new seastead receives a “grid‑assignment” request (from a master node or from a pre‑configured static IP if no master exists). It sets its autopilot destination to the assigned coordinate and approaches from outside the convoy.
  2. Approach → Half‑Grid Inside: When the node’s RTK‑GPS shows that it is within spacing/2 of the target coordinate, it enters a “pre‑join” state. It continues to broadcast heartbeat, but also begins to listen for “convoy‑mode‑allowed” messages from neighbours.
  3. Half‑Grid → Convoy‑Mode Activated: At least two neighbouring nodes must send a convoyModeAck message confirming the node is within half‑grid and its heading matches the convoy’s average heading (within ±5°). The autopilot then locks onto the target coordinate and maintains it using the same control loop as other nodes.
  4. Active → Leaving: A node can voluntarily leave by issuing a convoyLeave broadcast. The master (or any node) will update the grid occupancy map and, if needed, re‑assign the vacated spot.
The “half‑grid” threshold is tunable; larger spacing may require a larger threshold to avoid rapid toggling.

7. Safety & Failsafes

8. Example Code – Node‑Join Handshake (JavaScript / Node.js)

/**
 * Convoy node join state machine – simplified example
 * Assumes: node has access to `network` (WebSocket or UDP), `autopilot`, `rtk`
 */

const MSG_TYPES = {
  GRID_ASSIGN: 'gridAssign',
  CONVOY_ACK:   'convoyAck',
  HEARTBEAT:   'heartbeat',
  CONVOY_LEAVE:'convoyLeave'
};

class ConvoyNode {
  constructor(config) {
    this.id = config.nodeId;
    this.network = config.network; // abstraction of mesh socket
    this.autopilot = config.autopilot;
    this.rtk = config.rtk;

    this.state = 'idle'; // idle | approach | preJoin | convoy | leave
    this.targetPos = null;   // {x, y}
    this.gridSpacing = 30;  // meters
    this.heartbeatTimer = null;
    this.listeners = [];
  }

  /** Start listening for convoy messages */
  start() {
    this.network.on('message', this.handleMsg.bind(this));
    this.startHeartbeat();
    this.state = 'idle';
  }

  /** Send a heartbeat every 500ms */
  startHeartbeat() {
    const tick = () => {
      const msg = {
        type: MSG_TYPES.HEARTBEAT,
        nodeId: this.id,
        timestamp: new Date().toISOString(),
        position: this.rtk.position(),  // {x, y, z}
        heading: this.autopilot.heading(),
        watchStatus: { onWatch: this.isOnWatch() }
      };
      this.network.broadcast(JSON.stringify(msg));
    };
    this.heartbeatTimer = setInterval(tick, 500);
  }

  /** Process inbound messages */
  handleMsg(raw) {
    let msg;
    try { msg = JSON.parse(raw); } catch(e) { return; }

    switch (msg.type) {
      case MSG_TYPES.GRID_ASSIGN:
        if (this.state === 'idle') {
          this.targetPos = msg.targetPos;
          this.state = 'approach';
          this.autopilot.goTo(this.targetPos);
        }
        break;
      case MSG_TYPES.CONVOY_ACK:
        // Two ACKs required → transition to convoy
        this.onAckReceived(msg);
        break;
      case MSG_TYPES.CONVOY_LEAVE:
        this.handleLeave(msg);
        break;
    }
  }

  onAckReceived(ack) {
    // Keep track of ACKs (at least 2 from different neighbours)
    const ackKey = ack.fromNode;
    if (!this.acks) this.acks = new Map();
    this.acks.set(ackKey, ack);

    if (this.acks.size >= 2 && this.state === 'approach') {
      // Check distance to target
      const dist = this.rtk.distanceTo(this.targetPos);
      if (dist < this.gridSpacing / 2) {
        this.state = 'convoy';
        this.autopilot.lockPosition(this.targetPos);
        // Notify neighbours we are in convoy mode
        const notify = {
          type: MSG_TYPES.CONVOY_ACK,
          fromNode: this.id,
          targetPos: this.targetPos,
          timestamp: new Date().toISOString()
        };
        this.network.broadcast(JSON.stringify(notify));
      }
    }
  }

  /** Example of a human watch confirmation (simplified) */
  isOnWatch() {
    // In real code, query a UI button or biometric sensor
    return this._watchOn;
  }

  confirmWatch(operatorId) {
    this._watchOn = true;
    this._operator = operatorId;
  }

  handleLeave(msg) {
    this.state = 'leave';
    this.autopilot.releasePosition();
    this.network.broadcast(JSON.stringify({
      type: MSG_TYPES.CONVOY_LEAVE,
      nodeId: this.id,
      timestamp: new Date().toISOString()
    }));
  }
}

This script can run on a lightweight SBC (e.g., Raspberry Pi) using Node.js. It communicates over a UDP socket that the underlying Linux bridge (batman‑adv) forwards to all mesh nodes.

9. Future Enhancements

10. References & Resources


Authors Note: This document is a living design sketch. Please replace placeholder IP addresses, MACs, and secret keys with values generated during your on‑board provisioning process. All software referenced is open‑source and can be cloned from the indicated repositories.
```

Save the block above as convoy_mode.html and open it in any modern browser. The page is fully self‑contained (no external dependencies) and responsive (sidebar hidden on narrow screens). Feel free to embed it in your site, adjust the CSS colors to match your branding, or add JavaScript for interactive features like a live status dashboard.