Repository navigation
using the relay
Flash it, open the port, configure it, and push bytes over the radio — from bash, Python, or a serial terminal.
This page is the practical guide: how to get firmware onto a board and how to drive it from a host. For the exhaustive grammar see the Protocol Reference; to reach a board plugged into another machine, see the Relay Server.
Build produces a MICROBIT.hex. Flash it by copying it to the mounted
MICROBIT USB drive. Use cat, not cp — the drag-and-drop copy that cp
performs can race the board's mass-storage flasher and fail silently:
cat MICROBIT.hex > /Volumes/MICROBIT/MICROBIT.hex # macOS
# cat MICROBIT.hex > /media/$USER/MICROBIT/MICROBIT.hex # LinuxWhen two boards form a RAW250 link, both must run firmware built with the same
MICROBIT_RADIO_MAX_PACKET_SIZE (250 here). A mismatch silently drops the
larger packets on receive.
The relay runs at 115200 baud. Opening the host serial port toggles DTR, which resets the board and drops it back into the command plane (config is preserved). An in-place DTR toggle on an already-open port does not reset — the host must fully close and reopen.
Because the boot banner is emitted during the window the port is still being
opened, it is easily missed. The reliable pattern is: open the port, send
HELLO, then read the banner from the reply:
DEVICE:RADIOBRIDGE:relay:<deviceName>:<serialNumber>
Everything below is the command plane — line-oriented, \n-terminated, valid
only before !GO.
If you are driving a robot rather than exploring the firmware, you do not need any of this:
mbrelay connect tovezpicks a relay, tunes it to wherever the Relay Server's name registry says that robot is, and hands you a terminal on it.
HELLO # re-request the banner if you missed it
? # read back: # channel: <ch> group: <g> mode: <m> power: <p>
!C 5 # channel 5 (forces group 10)
!CG 55 108 # ...or an explicit channel and group. A robot's name only
# gives a DEFAULT address -- ask the relay server's registry
# (`mbrelay names`) where it actually is.
!MODE RAW250 # (default) headerless ≤250-byte framing
!ECHO ON # optional: make this board a transponder
!GO # enter the transparent data plane
<...bytes...> # everything after !GO is radio payload, both directions
# close + reopen the port to return to the command plane
Lines you receive from the relay are prefixed so a parser can classify them:
| Prefix | Meaning |
|---|---|
< |
a message received over the radio |
# |
comment / status / query reply from relay |
Common commands (full table in the Protocol Reference):
| Command | What it does |
|---|---|
!C <ch> |
channel 0–35, forces group 10 |
!CG <ch> <group> |
channel 0–83 and group 0–255 (!RC is an alias) |
!P <0-7> |
transmit power |
!MODE RAW250 |
headerless ≤250-byte framing (default) |
!MODE MAKECODE |
32-byte CODAL packets for a stock MakeCode robot |
!ECHO [ON|OFF] |
transponder: bounce every received message back (bare = toggle) |
!DEFAULTS |
clear saved config; compiled-in defaults on next reset |
!GO |
leave command plane, enter data plane (exit only by reset) |
? / !MODE?
|
query config / query mode |
Config changes apply immediately, persist to flash, and are echoed back as a #
comment.
The buttons work in either plane, no host needed:
- A = channel down, B = channel up (wrapping 0–35, only when on group 10).
-
A+B opens a menu on the 5×5 display. Each press advances; resting on an
item for 3 seconds accepts it. Items toggle packet mode (
32/25), toggle echo (ghost / west-arrow icon), or cancel (X).
The resting display shows the channel glyph (0–9, then A–Z for 10–35,
? for a custom group) — or a ghost icon when echo mode is on.
# stty configures the port; then write a command-plane send
stty -f /dev/tty.usbmodem* 115200 raw # macOS (use -F on Linux)
printf '> hello over the radio\n' > /dev/tty.usbmodem*PORT=/dev/tty.usbmodem*
stty -f $PORT 115200 raw
# read incoming lines in the background
cat $PORT &
# configure, then go transparent
printf '!MODE RAW250\n!GO\n' > $PORT
# from here, every byte is radio payload
printf 'streaming bytes with no prefix\n' > $PORTimport serial, time
port = serial.Serial("/dev/tty.usbmodem0001", 115200, timeout=1)
time.sleep(0.3) # let the DTR reset settle
port.write(b"HELLO\n") # ask for the banner
print(port.readline()) # b'DEVICE:RADIOBRIDGE:relay:...\r\n'
port.write(b"!C 5\n") # channel 5
port.write(b"?\n") # read back config
print(port.readline()) # b'# channel: 5 group: 10 ...'
port.write(b"!GO\n") # enter the data plane
port.write(b"payload bytes\n") # now transparent radio payloadA two-board end-to-end harness (discovery, reset, messaging, channel isolation,
throughput) lives at scripts/relay_test.py; the standalone-peer echo/MAKECODE
checks are the other scripts/*_test.py files.
Switch to MAKECODE mode and send text lines; a stock robot fires
on received string:
!MODE MAKECODE # 32-byte CODAL string packets
!CG 0 10 # match the robot's channel/group (MakeCode default group 1 → set as needed)
!GO
hello robot # a \n cuts each packet; lines >19 bytes truncate (or !FRAG ON to fragment)
Note MAKECODE caps a single string at 19 bytes. Longer lines truncate by
default; !FRAG ON fragments them, but only another board running this
firmware can reassemble — a stock robot cannot. See the
Protocol Reference §4–5 for the framing detail.