Skip to content

Iridium Bluetooth service ​

The MeshSat node exposes this service next to the Meshtastic service, on the same Bluetooth LE connection. It is a binary-safe serial line to the node's RockBLOCK 9603. The client speaks the modem's AT commands through it, exactly as it would over a cable. MeshSat Android is the client we build and test against.

Contract version 2, since the firmware of 27 September 2026. A version 1 client still works: the first two bytes of STATUS mean what they always did.

CharacteristicUUIDPropertiesCarries
Serviceb3d305a2-7310-4877-ad12-8e245e71951a
RXb9e2d4ba-f386-4728-b77a-7df7121db7a9write, write without responseraw bytes to the modem's UART
TX469354dc-4c89-41ed-b939-d707c7a11f49notify, readraw bytes from the modem, in chunks of up to the MTU minus 3 (at most 244)
STATUS69a4064d-78b9-46e5-a30a-1862e553245aread, notifyfour bytes: the contract version (02), the modem's owner (00 none, 01 phone, 02 node), the flags byte and the signal byte described under STATS. Notified when the owner or the flags change

Owning the modem ​

  • Subscribing to TX notifications takes the modem when nobody owns it. STATUS then reads owner 01 and is notified.
  • Unsubscribing from TX, or disconnecting, gives it back (owner 00).
  • Bytes written to RX while the client does not own the modem are discarded, so nothing stale reaches the modem later. Wait for owner 01 before the first write.
  • Modem output while nobody owns it is discarded.
  • Owner 02 is the node itself. When no client is subscribed to TX, the node's own routing takes the modem for the mesh channel it carries over Iridium. It hands the modem to a client within about a second of the TX subscription, between its own AT commands, and only after the result when one of its satellite sessions is in flight, which can take up to 90 seconds. A client that reads owner 02 should wait, not fail.

What changes hands with the modem ​

The modem holds one outgoing message and one incoming message, and both stay in it when the owner changes.

  • A client on the pipe is the gateway. While a client is subscribed to TX, the node does not queue the texts it hears on its Iridium channel. The client sends them if its own rules say so. Texts the node queued before the client subscribed stay in the node's queue for up to 30 minutes and go out when the node has the modem again.
  • Before the node hands the modem over, it clears its own outgoing message from the modem, so the client's next session cannot send it. The text stays in the node's queue.
  • When the node takes the modem, it clears the outgoing buffer. A client keeps its own queue and writes its message again when it has the modem back.
  • When the node takes the modem, it does not clear the incoming buffer. It asks AT+SBDSX. If a message is there, the node reads it, broadcasts it on its Iridium channel and only then clears it. So a message that a client's session brought in and the client did not read is not lost. A client that wants such a message for itself reads it before it lets go, or takes the modem back within a minute, which is how long the node waits after a client leaves.
  • A message the node cannot read, or cannot broadcast because it has no Iridium channel, stays in the modem. After taking the modem, a client asks AT+SBDSX and reads a message it finds there.

The serial line ​

  • The pipe adds and removes nothing. The link to the RockBLOCK is 19200 baud, 8N1.
  • AT commands end in a single carriage return. The 9603 does not accept a line feed as the terminator.
  • The modem's stored profile has echo on. A client that sends ATE0 keeps echo off until the modem next loses power.
  • On node v1 the modem is powered at boot and answers about 10 seconds later. Retry the first command until it does.
  • Every satellite session opened through the pipe (AT+SBDIX and friends) is billed by the airtime provider, even one with nothing to send.

Version 2 additions ​

Two more characteristics on the same service. A client that ignores them keeps working.

CharacteristicUUIDPropertiesCarries
STATS9c22cf07-2256-4fc2-b6ee-ab0ceb12198dread, notify64 bytes of node health, little-endian, notified on change and at most every 2 s (52 before 2 November 2026)
PASS5c1000e8-f411-4f3d-a4c9-5ee0610a8e66writepass windows from the client, up to eight
CONTROLd6a8a903-6af1-4530-ab3d-49c39b636e04writethe owner's switches for the node's status LED and screen (since 24 October 2026)

STATS, byte by byte:

OffsetTypeField
0u8version, 02
1u8owner, as in STATUS
2u8flags: bit 0 a session is in flight, bit 1 a message waits at the gateway, bit 2 the modem answers, bit 3 the incoming buffer is nearly full, bit 4 the modem speaks JSPR (a RockBLOCK 9704; clear on a 9603 and on a node older than 8 October 2026), bit 5 the owner switched the status LED off, bit 6 the owner switched the screen off (bits 5 and 6 since 24 October 2026), bit 7 the satellite beacon is on (since 2 November 2026)
3u8signal 0 to 5 as the modem last reported it, FF when never read
4u32age of that signal reading in seconds, FFFFFFFF when never read
8u32satellite sessions since boot, by any owner
12i16MO status of the last session, -1 before the first
14u16MOMSN of the last session
16i16MT status of the last session, -1 before the first
18u16messages still queued at the gateway after the last session
20u32age of the last session in seconds, FFFFFFFF before the first
24u32uptime in seconds
28u32reboots by the node's Bluetooth watchdog, lifetime
32u32bytes a client wrote faster than the modem took them, since boot
36u32sessions opened by the node's own routing since boot
40u32messages the node's own routing sent
44u32messages the node's own routing received
48u8the node's own sessions today
49u8the node's daily cap
50u8why the node's previous run ended, see below. Zero on firmware before 2 October 2026
51u8battery at that stop in units of 100 mV, when the stop was planned; zero when not recorded
52u8the satellite beacon: 0 not in use (the node's panel switch was never pressed, or a node without one), 1 off, 2 on, 3 off and sending its last message
53u8why the beacon last went off: 0 not since the node started, 1 the switch, 2 15 minutes without satellite traffic, 3 the 30 minute limit, 4 the cell too low for the modem, 5 switched off before any of its messages reached the modem, so nothing was sent
54u16beacon messages the modem has taken since the node started
56u32the uptime in seconds when the beacon last went on, FFFFFFFF when it has not since the node started
60u16seconds between the beacon's position messages (600)
62u16seconds the beacon stays on at most (1800)

Why the previous run ended: 0 not recorded, 1 power loss or reset (the cell was pulled or collapsed, the reset button, or the reset a flashing tool ends with; the chip reports them alike), 2 a reset over USB while the node ran, 3 a restart the firmware asked for, 4 the Bluetooth watchdog, 5 crash, 6 watchdog timer, 7 brownout, 8 low battery (the node shut itself off), 9 switched off, 10 sleep. A client shows a value it does not know as its number.

Bytes 52 to 63 since 2 November 2026: a client accepts a STATS of 52 bytes or more and reads only what is there. While the beacon is on, its next position message is due at the uptime of byte 56 plus a whole number of intervals, and it goes off by itself at that uptime plus byte 62 at the latest. None of these fields moves on its own, so they add no notifications.

The signal byte is information for a screen. It is never a reason to hold a send: this modem has sent and received at 0.

STATS is rebuilt every 2 seconds and a read always returns the current value. A notification goes out only when something other than the three moving fields changed: the signal age, the last session age and the uptime are excluded from that comparison, so an idle node stays quiet on the air. A client that shows those ages re-reads the characteristic while they are on screen.

PASS, written with response: 01, then the number of windows (at most 8), then for each window a u32 start as Unix seconds, a u16 duration in seconds and a u8 peak elevation in degrees, all little-endian. A write replaces the node's list. The node's own routing then opens routine sessions only inside a window; a ring alert or a message queued at the gateway still goes at once, and a node that never received a list is not held back. Writes are accepted from any client on the service, whoever owns the modem.

STATUS carries the flags byte and the signal byte after the owner since version 2. Clients accept a STATUS of 2 or 4 bytes and read only what is there, and they ignore flag bits they do not know: bit 4 was added on 8 October 2026 so a client can pick its 9603 or 9704 driver from the node's own word instead of a setting.

CONTROL, written with response: pairs of a switch byte and a value byte, 01 the status LED, 02 the screen, value 00 on and 01 off; several pairs may go in one write, anything else is ignored. The node keeps both switches across restarts and reports them in STATUS flags bits 5 and 6, which is where a client reads them back. With the LED off the node's satellite-state logic keeps running and shows again when the LED is switched on. With the screen off the display stays dark through messages and banners until the switch goes back on or someone presses the node's own button, so a node is never left dark without a phone. A node older than 24 October 2026 has no CONTROL characteristic.

Security ​

If the node's Bluetooth pairing mode is anything other than "no PIN", all six characteristics need an encrypted, authenticated link: the same bond as the Meshtastic service. With "no PIN" they are open, like the Meshtastic service.

Changes ​

Any change to this contract bumps the version byte in STATUS, and MeshSat Android is updated first, with MeshSat iOS following it. Additions that a version 1 client can ignore, like STATS, PASS and CONTROL, ship before the bump.

Adding or removing a characteristic changes the attribute table, and a bonded phone keeps a cached copy of the old one. For the twenty boots after such a change the node indicates Service Changed to every authenticated peer as soon as the link is up, so the phone discovers the table again. A client may also drop its own cache when the service comes back without a characteristic it expects. The source of truth is the firmware: src/meshsat/IridiumPipe.h in meshsat-firmware.