Skip to content

Connections

A beebo node offers three ways in: BLE, WiFi (TCP) and USB. This page explains how the firmware decides which one is talking to the node, what it does when clients come and go, and what you can rely on. None of it needs configuration. It is here so that the behavior you see in the app, such as a second client being refused, makes sense.

What is on by default

Transport Default Notes
BLE on Pairing PIN 123456 (node.ble.pin).
USB on Companion session over the cable, beside BLE or WiFi.
WiFi (TCP) off Needs an SSID and password first, which the MeshCore app or the beebo command line can set. The node listens on TCP port 5000.

BLE and WiFi share the board's single 2.4 GHz radio and are mutually exclusive: turning one on turns the other off. USB is independent of both. At least one transport is always available at boot, so a node can never be left unreachable. If a change would switch off the last transport, USB is turned on instead. The WiFi credentials are entered from the app or the command line over USB or BLE.

One conversation at a time

The app protocol is a single conversation, so the node gives it to exactly one transport. All enabled transports listen while the node is idle. The first one that delivers a real command frame becomes the session owner, and the others are held back until it disconnects.

  • A second client is turned away, not queued. A phone that connects over BLE while a laptop is on USB does not get a half-working link: the node drops the stray connection. A brand new connection is given a short grace period (300 ms, 3 s for BLE) to send its first frame, so a client that is merely slow is not mistaken for a stray.
  • USB does not starve wireless. A USB session can be pre-empted if BLE or WiFi wants the lock.
  • Closing releases the lock. When the owner disconnects, the lock is released and every transport listens again. Only the transport that was used is cycled; the others stay up.

Two state machines

The firmware models the problem with two small state machines instead of scattered flags, which is why the behavior above is predictable.

Link state answers "is this transport's radio on?". BLE and WiFi are one state variable, so a state that means "BLE and WiFi both up" does not exist.

State Meaning
OFF Neither BLE nor WiFi is up.
BLE_UP_WAIT, BLE_UP, BLE_OFF_WAIT BLE is starting, up, or waiting to be torn down.
TCP_UP_WAIT, TCP_UP, TCP_BACKOFF, TCP_OFF_WAIT WiFi is joining, up (an IP and an open listener), waiting before it retries after a lost connection, or waiting to be torn down.

USB has its own small state (OFF, UP, OFF_WAIT) because it needs no radio.

A wait state is what keeps a switch from cutting its own reply. If you turn WiFi off from a session that is running on WiFi, the link stays fully up until that session ends, and only then is it taken down. The same goes for turning BLE on while WiFi is in use. Requests from another transport apply at once. One function brings each radio up and one takes it down, so the sequence is the same every time, and a lost WiFi connection is retried from one place only, after a backoff.

Session state answers "who owns the conversation?".

State Meaning
DISABLED No transport is serviced.
IDLE Every enabled transport is polled for a first frame.
ACTIVE One transport owns the session. Every other one is still polled, and evicted if it reports itself connected.
CLOSING The owner asked to disconnect. The node waits for its last bytes to leave.

Reconnecting and dead clients

Phones drop off the network without saying goodbye. For WiFi:

  • A reconnecting phone replaces its own stale session. A new connection from the same IP address as the current client takes over at once.
  • A different IP is refused and never pre-empts the session.
  • A client that disappears is dropped in about 25 seconds. TCP keepalive probes it after 10 seconds of silence, three times, 5 seconds apart. Until then, a reconnect from the same address still works.

Closing cleanly

When an app asks to disconnect, the node first sends its answer, then waits for it to drain before it drops the link (up to 250 ms). BLE waits for two connection events, because a notification only leaves the radio at one. Without that wait, about a quarter of BLE sessions ended with a GATT "Unlikely Error" on the phone's final write.

BLE pairing

BLE uses a static passkey with display-only I/O, so there is no screen: the app asks for the PIN, you type 123456 (or your own node.ble.pin), and the node bonds with secure connections.

On a first pairing you take several seconds to type the PIN, and the app starts its own 5-second command timeout as soon as it subscribes. Beebo makes the subscription wait until the link is encrypted, so the app does not time out and report "Failed to fetch device info". A bonded reconnect never asks for the PIN and connects in about half a second.

If the app cannot connect after you flash beebo over firmware you paired with before, remove the node from the phone's Bluetooth list and pair again: bonds made by an older BLE stack do not carry over.

USB

USB needs no pairing, but it has its own pitfall: when the host stops reading, the USB driver can discard queued bytes after 100 ms and still report the writes as done. Beebo only writes a frame that fits in the transmit buffer, so large downloads (contacts, neighbors, the monitor ring) cannot be silently truncated. A USB session is considered gone after 30 seconds without a byte.

Looking inside

The node keeps a ring of connect, disconnect and lock events. Stream it with beebo monitor debug, or read the detail in the firmware internals. From a phone, the troubleshooting table maps what you see to a cause.