Skip to content
EN

Discovery & targeting

Sending is unicast per device by default; broadcast is the fallback only while no device has been found yet.

hb.begin(7700, "MyDevice"); // open the UDP socket; app name (<=16 chars) shows on the OLED

When the app name appears on the Hapbeat OLED you are connected. On exit, call hb.end() to announce leaving (clears the OLED app name).

hb.begin(7700, "MyDevice");
hb.discover(1500); // broadcast PING -> register every device that replies
void loop() {
hb.ping(); // every few seconds — keeps unicast destinations alive
// ...
}

discover() waits out the whole timeout and registers every device that replies (up to 8). From then on play / stop / stopAll and streaming unicast to all of them. If nobody replies, sending falls back to broadcast as before.

Why broadcast is slow: whenever a single power-saving client is associated with the AP, the AP buffers group-addressed frames until its next DTIM beacon (100–300 ms). Nothing on the Hapbeat can avoid it — the cause is an unrelated client. Broadcast frames also get no MAC-layer ACK/retry and go out at the lowest rate. Unicast avoids both.

Devices leave the table if they go quiet for longer than the liveness window (15 s default), so call ping() every few seconds from loop(). Once the table empties, sending returns to broadcast automatically.

APIPurpose
hb.deviceCount()live known devices (0 = sends broadcast)
hb.deviceIp()first known device IP (single-device sketches)
hb.setDeviceIp(ip)pin an IP (no discovery needed, never expires); 0.0.0.0 clears
hb.poll()consume arrived PONGs (optional — sends do it too)
hb.setDeviceTimeout(ms)liveness window (default 15000)
hb.setBroadcastOnly(true)always broadcast

The target argument of play / playSine / etc. selects the destination. The device applies the same check on receipt, so however the packet is routed, a device the target excludes never fires.

Device addresses are always player_<N>/<position>/group_<M> (defaults player_1 / group_1). Matching is positional: segment i of the target is compared only with segment i of the address.

targetmeaning
""every device
"player_1"every device of player 1 (front match)
"player_1/pos_neck"just that position
"*/pos_neck"that position on every player
"*/*/group_2"devices in group 2
hb.play("sample-kit.sine_100hz", 0.6f, "player_1/pos_neck");
hb.stopAll("*/*/group_2");

hb.setGroup(n) is the sender’s group id carried in CONNECT_STATUS — a different thing from the group_<N> segment of a target.