You now understand the theory behind how Bluetooth LE connections work. Let's put it into practice and connect your two dongles for the first time. By the end of this lesson, you'll have established a connection, verified it from both sides, and performed a clean disconnect.
Everything you've learned so far comes together in this lesson. Advertising, scanning, and roles all play a part in establishing a connection.
Let's get the peripheral dongle ready. Switch to peripheral mode, set a recognizable name in the advertising data, and start advertising:
ATR
Wait ~15 seconds for the dongle to reboot and your serial terminal to reconnect (the USB port may re-enumerate), then:
AT+PERIPHERAL
OK
AT+ADVDATA=0B:09:50:45:52:49:50:48:45:52:41:4C
OK
ADVERTISING DATA: 0B095045524950484552414C
AT+ADVSTART
Advertising type: GAP_CONN_MODE_UNDIRECTED Advertising interval minimum: 1100 maximum: 1100
ADVERTISING...
The peripheral is now broadcasting with the name "PERIPHERAL" on the three advertising channels. If you completed Module 3, this should feel familiar.
On the central, reset to a clean state:
ATR
Switch to central mode:
AT+CENTRAL
OK
Scan for the peripheral. You'll need the peripheral dongle's MAC address for this command; if you don't have it handy, run AT+GETMAC on the peripheral first to see it (the response line starts with OWN MAC ADDRESS:). Substitute that address wherever <PERIPHERAL-ADDRESS> appears:
AT+SCANTARGET=[0]<PERIPHERAL-ADDRESS>=3
SCANNING TARGET DEVICE...
[<PERIPHERAL-ADDRESS>] Device Data [ADV]: 0201060B095045524950484552414C
[<PERIPHERAL-ADDRESS>] Device Data [RESP]: 0709426C6575494F
[<PERIPHERAL-ADDRESS>] Device Data [ADV]: 0201060B095045524950484552414C
[<PERIPHERAL-ADDRESS>] Device Data [RESP]: 0709426C6575494F
[<PERIPHERAL-ADDRESS>] Device Data [ADV]: 0201060B095045524950484552414C
[<PERIPHERAL-ADDRESS>] Device Data [RESP]: 0709426C6575494F
SCAN COMPLETE
There's your peripheral in the scan results. Now let's connect to it. You have two options:
Option A: Connect by MAC address. Use the full address with the address type prefix in brackets:
AT+GAPCONNECT=[0]<PERIPHERAL-ADDRESS>
Trying to connect...
CONNECTED.
handle_evt_gap_connected: conn_idx=0000 address=<PERIPHERAL-ADDRESS> CI max is 24.
Target conn indx changed to=0000
Peripheral exchanged tx data length is 251, rx data length is 251.
Peripheral updated CI min is 24, CI max is 24.
0001 serv 0x1800
0002 char 0x1800 prop=02 (-R------)
0003 ---- 0x2a00
0004 char 0x1800 prop=02 (-R------)
0005 ---- 0x2a01
0006 char 0x1800 prop=02 (-R------)
0007 ---- 0x2a04
0008 char 0x1800 prop=02 (-R------)
0009 ---- 0x2aa6
000a serv 0x1801
MTU CHANGED: 512
Peripheral updated CI min is 24, CI max is 24.
MTU CHANGED: 512
000b serv 0783b03e-8535-b5a0-7140-a304d2495cb7
000c char 0783b03e-8535-b5a0-7140-a304d2495cb7 prop=10 (----N---)
000d ---- 0783b03e-8535-b5a0-7140-a304d2495cb8
... (55 more lines trimmed for readability) ...
handle_evt_gattc_browse_completed: conn_idx=0000 status=0
handle_evt_gattc_write_completed: conn_idx=0000 handle=0014 status=0
handle_evt_gattc_notification: conn_idx=0000 handle=0014 length=1
Value received:
Hex: 0x01
Size: 1
If the connection hangs for more than a few seconds, the peripheral is probably not advertising or you have the wrong address. Run AT+CANCELCONNECT to abort the attempt and get back to a clean state:
AT+CANCELCONNECT
OK
Then check: is the peripheral advertising? (AT+GAPSTATUS on the peripheral should show "Advertising"). Is the address correct? (Run AT+GETMAC on the peripheral to verify).
If you see CONNECTED. followed within a second or two by a disconnect that reports reason: 08, that's HCI error code 0x08 (Connection Timeout), meaning the Link Layer's supervision timeout fired because the controller didn't see any packets from the peer in the configured window (1 second by default on BleuIO connections; we'll cover this parameter in Lesson 4.3). It's the controller's way of saying "the link went silent, I'm tearing it down."
In practice, this almost always points to a wedged firmware state on one of the dongles, often after a long idle period or a previous bad disconnect. The fix is usually ATR: it does a soft reset of the firmware that clears the state machine cleanly. I've seen cases where physically unplugging and replugging the dongle alone isn't enough on its own; ATR reaches state that a hardware power cycle doesn't always reach.
To recover, in this order:
ATR on each dongle. Wait ~15 seconds for the dongle to reboot and your serial terminal to reconnect (the USB port may re-enumerate). This clears it most of the time.ATR alone doesn't clear it, physically unplug both dongles, wait ~10 seconds, plug them back in (avoid USB hubs; plug directly into the laptop), then run ATR again.AT+PERIPHERAL / AT+CENTRAL.If the disconnect persists after ATR and a clean replug, your laptop's USB power management may be suspending the bus (more common on Windows laptops on battery power) or a USB hub between the laptop and the dongles is dropping packets. Switching to a different USB port often clears it. We'll come back to disconnect reason codes more systematically in Lesson 4.5.
Option B: Connect by scan result index (shortcut). If you've just run a broad scan with AT+GAPSCAN=N (as you did in Module 1) and the peripheral appeared as result [01], you can connect by that index instead of typing the full MAC:
AT+GAPCONNECT=1!
Trying to connect...
CONNECTED.
The ! tells the dongle to look up the address from the most recent AT+GAPSCAN results. Note that AT+SCANTARGET (used in Step 2 above) doesn't produce numbered results, so the ! shortcut only works after a broad scan. I recommend using the MAC address method when you know the exact address, and the index shortcut for quick testing during development.
Important: The
[0]prefix means a public address (manufacturer-assigned). If a device shows[1]in scan results, it uses a random address, and you must connect with[1]instead. Using the wrong address type will cause the connection to fail. We'll explore this more in Lesson 4.5.
When the connection succeeds, the central terminal will display a significant amount of output. Don't be alarmed; I've seen this wall of text confuse many beginners, but it's all normal. Let's walk through what you'll see.
Central (white): Here's what the connection output looks like:
CONNECTED.
handle_evt_gap_connected: conn_idx=0000 address=<PERIPHERAL-ADDRESS> CI max is 24.
Target conn indx changed to=0000
Peripheral exchanged tx data length is 251, rx data length is 251.
0001 serv 0x1800
0002 char 0x1800 prop=02 (-R------)
0003 ---- 0x2a00
0004 char 0x1800 prop=02 (-R------)
0005 ---- 0x2a01
0006 char 0x1800 prop=02 (-R------)
0007 ---- 0x2a04
0008 char 0x1800 prop=02 (-R------)
0009 ---- 0x2aa6
000a serv 0x1801
Peripheral updated CI min is 24, CI max is 24.
MTU CHANGED: 512
000b serv 0783b03e-8535-b5a0-7140-a304d2495cb7
000c char 0783b03e-8535-b5a0-7140-a304d2495cb7 prop=10 (----N---)
000d ---- 0783b03e-8535-b5a0-7140-a304d2495cb8
000e desc 0x2902
000f desc 0x2901
...
handle_evt_gattc_write_completed: conn_idx=0000 handle=000e status=0
handle_evt_gattc_notification: conn_idx=0000 handle=0014 length=1
Value received:
Hex: 0x02
Size: 1
handle_evt_gattc_browse_completed: conn_idx=0000 status=0
handle_evt_gattc_write_completed: conn_idx=0000 handle=0014 status=0
handle_evt_gattc_notification: conn_idx=0000 handle=0014 length=1
Value received:
Hex: 0x01
Size: 1
That's a lot of text scrolling by. Here's what's happening:
CONNECTED. confirms the connection was established.CI max is 24 shows the connection interval in raw Bluetooth LE units. The value 24 means 24 x 1.25 ms = 30 ms, which is the default connection interval.0001, 0002, etc.) is the dongle automatically discovering the peripheral's GATT services and characteristics. This happens because auto-discovery (ATDS) is enabled by default.MTU CHANGED: 512 indicates the Maximum Transmission Unit was negotiated to 512 bytes.Hex: 0x01 and Hex: 0x02 notifications are flow control messages from the dongle's built-in Serial Port Service (SPS). These are not errors; the auto-discovery feature pre-enables SPS notifications.Tip: If you'd prefer cleaner connection output without the automatic service discovery flood, you can disable it with
ATDS0. For this course, I recommend leaving it on since it's useful to see what services the peripheral offers. You can always runAT+GETSERVICESmanually after connecting (which we'll do in Module 5).
Let's confirm the connection is active on both dongles.
AT+GAPSTATUS
Central role
Connected
Not Advertising
AT+GETCONN
[0]<PERIPHERAL-ADDRESS>, conn_idx=0000, Our Role: Client, bonded=false, paired=false
AT+GAPSTATUS
Peripheral role
Connected
Not Advertising
AT+GETCONN
[0]<CENTRAL-ADDRESS>, conn_idx=0000, Our Role: Server, bonded=false, paired=false
Notice a few things:
AT+GETCONN shows each dongle's view of the connection: the central sees itself as "Client" and the peripheral sees itself as "Server." These are the GATT roles we'll explore in Module 5.Key Takeaway: A Bluetooth LE connection is always a two-way relationship. Both devices are aware of the connection and both can verify it independently. The central initiated the connection, but once established, data can flow in both directions.
On the central, perform a clean disconnect:
AT+GAPDISCONNECT
handle_evt_gap_disconnected: conn_idx=0000 address=<PERIPHERAL-ADDRESS>.
DISCONNECTED.
Confirm the central is back to a disconnected state:
AT+GAPSTATUS
Central role
Not Connected
Not Advertising
Now switch to the peripheral and check its state:
AT+GAPSTATUS
Peripheral role
Not Connected
Advertising
Notice that the peripheral automatically resumed advertising after the disconnect. This is helpful behavior: you don't need to run AT+ADVSTART again. The peripheral is ready for another connection without any manual intervention.
Key Takeaway: Establishing a Bluetooth LE connection follows the same pattern every time: peripheral advertises, central scans, central connects by address, both enter the connected state. When the central disconnects, the peripheral resumes advertising automatically.
| Command | What It Does |
|---|---|
AT+PERIPHERAL | Switch to peripheral role |
AT+ADVDATA=<hex> | Set advertising data |
AT+ADVSTART | Start advertising |
AT+CENTRAL | Switch to central role |
AT+GAPSCAN=N | Scan for N seconds |
AT+GAPCONNECT=[type]address | Connect to a device by MAC address |
AT+GAPCONNECT=N! | Connect to scan result index N |
AT+GAPSTATUS | Check role and connection state |
AT+GETCONN | List connected devices with details |
AT+GAPDISCONNECT | Disconnect from the connected device |
ATDS0 / ATDS1 | Disable/enable auto-discovery on connect |
In this lesson, we:
AT+GAPSTATUS and AT+GETCONNYour dongles can now connect and disconnect on command. But the connection used default parameters (30 ms interval, no latency, 1000 ms timeout). In the next lesson, we'll explore what these connection parameters mean and how they control the behavior of your connection.