> ## Documentation Index
> Fetch the complete documentation index at: https://docs.efference.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect to an Efference Device

> Discover an M1, select USB or BLE, open a session, and inspect its capabilities

Every SDK workflow begins with an `ef::Device`. Choose a control transport,
fill `InitParameters`, and call `open()` before using a device feature.

## Discover devices

USB discovery is always available:

```cpp theme={null}
std::vector<ef::DeviceProperties> devices =
    ef::Device::get_device_list();
```

Include a Bluetooth LE scan with:

```cpp theme={null}
std::vector<ef::DeviceProperties> devices =
    ef::Device::get_device_list(true, 3000);  // scan BLE for 3 seconds
```

USB results provide `device_id` and `serial`. Bluetooth LE (BLE) results
provide `ble_address` and `ble_name`.

From the command line:

```sh theme={null}
ef-cli list
ef-cli list --scan-ble
```

## Open over USB

USB is the default transport:

```cpp theme={null}
ef::Device device;
ef::ERROR_CODE status = device.open();
```

Select a specific M1 when several are attached:

```cpp theme={null}
ef::InitParameters init;
init.input_type = ef::INPUT_TYPE::USB;
init.device_id = 1;

ef::Device device;
ef::ERROR_CODE status = device.open(init);
```

USB powers the M1 and carries control and live data.

## Open over Bluetooth LE

BLE provides the control plane:

```cpp theme={null}
ef::InitParameters init;
init.input_type = ef::INPUT_TYPE::STREAM;
init.ble_address = "AA:BB:CC:DD:EE:FF";
init.ble_password = "123456";  // factory default

ef::Device device;
ef::ERROR_CODE status = device.open(init);
```

Leaving `udp_host` empty creates a control-only session.

An incorrect password does **not** cause `open()` to fail. The device continues
to answer `info`, `state` and `storage` unauthenticated, and these are required
to diagnose a device whose password has been lost, so failing here would place
them behind the credential being recovered. The gated verbs report
`INVALID_PASSWORD` individually. Check in advance with
`device.is_authenticated()`, which is meaningful on any BLE link and on a USB
link the device reports as locked. See [Access Control and
Encryption](/device/security).

## Add wireless live data

Provision the M1 on WiFi first, then set the receiving Linux host's reachable
IP address:

```cpp theme={null}
ef::InitParameters init;
init.input_type = ef::INPUT_TYPE::STREAM;
init.ble_address = "AA:BB:CC:DD:EE:FF";
init.ble_password = "123456";
init.udp_host = "192.168.1.50";  // this host, not the M1
init.udp_port = 5005;

ef::Device device;
ef::ERROR_CODE status = device.open(init);
```

The M1 cannot infer the host IP over BLE. Both devices must be on a reachable
network, and the host firewall must permit the selected UDP port.

## Session behavior

`open()` claims the control transport, validates capture settings, aligns the
device clock, and caches device information. It normally leaves the handle in
`IDLE`, though pre-existing device activity may report `STREAMING`.

The first `grab()` starts the USB or WiFi/UDP data plane, so control-only work
does not start a live stream unnecessarily.

MCAP replay is the exception: `open()` creates the reader and reports
`STREAMING`.

## Inspect identity and capabilities

`get_device_information()` returns the snapshot cached by `open()`:

```cpp theme={null}
ef::DeviceInformation info = device.get_device_information();

std::cout << info.serial << "\n";
std::cout << info.firmware_version << "\n";

for (const ef::SupportedMode& mode : info.capabilities.modes) {
    std::cout << mode.resolution.width << "x"
              << mode.resolution.height << " @ "
              << mode.fps << "\n";
}
```

This cached accessor does not block or access the device. The snapshot is
captured at `open()` and updated by the WiFi calls, so a handle held open across
a network change continues to report the values captured earlier. Request an
update explicitly when current values are required:

```cpp theme={null}
if (dev.refresh_device_information() == ef::ERROR_CODE::SUCCESS)
    info = dev.get_device_information();   // copies taken earlier are unaffected
```

If the transport is unavailable at the time of the refresh, the WiFi association
and the BLE link are reset to unknown rather than retaining their previous
values, so a disconnected device does not continue to report itself as
connected. The health status is retained, because it records a sweep that
completed.

## Close the session

```cpp theme={null}
device.close();
```

Closing stops a host live stream and finalizes a host-file recording, then
releases the transport. It does **not** stop a device-local recording, which is
designed to continue across host disconnects.

Calling `open()` on an already open handle returns
`INVALID_FUNCTION_CALL`. Close it before opening a different transport or
device.

## Common connection errors

| Error                      | Meaning                                                                                 |
| -------------------------- | --------------------------------------------------------------------------------------- |
| `DEVICE_NOT_DETECTED`      | No matching USB device, BLE device, or MCAP file                                        |
| `INSUFFICIENT_PERMISSIONS` | USB device found but blocked by host permissions                                        |
| `DEVICE_NOT_AVAILABLE`     | Device found but could not be opened                                                    |
| `DEVICE_BUSY`              | Another process already holds this device over USB                                      |
| `INVALID_FUNCTION_CALL`    | Invalid state, missing BLE support, missing required path/address, or repeated `open()` |
| `COMMUNICATION_ERROR`      | Control transport failed during the session                                             |

USB control permits one client per cable. While an SDK application or `ef-cli`
holds the device open, a second USB `open()` is refused with `DEVICE_BUSY`. BLE
remains available in parallel, so a long-running USB application does not prevent
operator access to the device.

`INVALID_PASSWORD` is deliberately absent from this table: authentication failure
is not reported by `open()`, only by the gated verb called subsequently.
