Multi-Slave Multi-Master Dual-Topology Example#
Background#
This code example has related User's Guides:
Description#
This code example demonstrates how to handle multiple (simultaneous) connections, when the device has to act as master on some of the connections, and as slave on the rest of the connections.
A typical use case may be when data is gathered from multiple peripheral devices (slaves), and then the aggregated data is provided to a master device, e.g., to a smartphone. Therefore, this example implements the functionality of gathering temperature values from different thermometers, and providing them toward a smartphone in one GATT service.
The device discovers and connects to nearby devices implementing the Health Thermometer service and reads their temperature values. Meanwhile, the device is also advertising to make it possible for smartphones to connect to it, and read the aggregated data.
Connecting to Slaves (Device as a Master)#
In the le_gap_scan_response event, this example is looking for devices advertising the Health Thermometer Service (UUID: 0x1809) and it is trying to connect to them (if there is no another connect operation already ongoing). During the device connecting (and discovering GATT services), new scan responses are blocked using state variables to handle one connection operation at a time (atomic). Even if it is still only connecting (no le_connection_opened event yet), it is counted as a connection so that the number of connections would not exceed maximum.
If the number of maximum connections is reached, the scanning/discovery is stopped and the advertisements are made non-connectable (the device name can still be seen but it cannot be connected anymore).
When the connection is initiated, a 10 second timeout timer is also initiated, in case the connection didn't go through successfully (e.g., slave device goes out of range). When the timer expires, it will cancel the connection operation by using gecko_cmd_le_gap_end_procedure() (and that will trigger the le_connection_closed event restarting scanning/discovery again if required).
Connecting to Masters (Device as a Slave)#
As master connections are created and le_connection_opened event triggered, the number of connections needs to be checked to see if the maximum number of supported connections is reached. If it has not been reached, resume advertising (setting back as general discoverable and connectable scannable) because that is automatically stopped when a master connects (scanning is not affected by master connections so that will continue uninterrupted). If MAX_CONNECTIONS is reached, then the device must advertise as non-connectable.
Disconnections#
If a disconnection occurs (le_connection_closed event), the remaining number of connections is checked to see if it is maximum - 1, in which case scanning and advertising can be resumed to allow a new connection to form.
Data Gathering#
If the device role on a newly opened connection is 'master', the device is connected to a thermometer peripheral device, from which temperature values will be read out. To get temperature values:
Discover the Health Thermometer service has to be discovered.
Discover the Temperature Measurement characteristic has to be discovered.
Enable indications on the Temperature Measurement characteristic.
Therefore, the example applications goes through these steps. After indications are enabled, the new temperature values provided by the thermometers will trigger a gatt_characteristic_value event. In this event handler the central device will forward the indicated value to all those master devices that have subscribed to indications. To differentiate between values received from different thermometers, the central device implements multiple Temperature Measurement characteristics in one Health Thermometer service.
Setting up#
To try this example, you need multiple radio boards, one for the central device (data aggregator) and one or more for the peripherals (thermometers).
Central Device#
Create a new SoC-Empty project for your device.
Copy the attached app.c and app.h files into your project overwriting the existing ones.
Set DEBUG_LEVEL to 1 in app.h.
Open the GATT Configurator and import the attached gatt.xml file with the import button found on the right side.
Press Save and Generate in the GATT Configurator.
Build and flash the project to your device.
Peripherals#
Create a new SoC-Thermometer project for your device.
Build and flash your project to your device.
Usage#
The code example prints out debug information over the WSTK VCOM @ 115200 baud (without flow-control). As you start connecting to slave and master devices, you should see a debug log as depicted below.
Log file from the terminal application:
stack version: 2.13.0
local BT device address: 00:0b:57:ef:2d:17
Scanning
Advertising - device name is "MSMMDT" (short for Multi-Slave/Multi-Master/Dual-Topology)
HTM device found
Initiate connection
NEW CONNECTION
Role: Master
Handle: #1
Total connected devices: 1
Number of connections left: 7
Indications enabled
HTM device found
Initiate connection
NEW CONNECTION
Role: Master
Handle: #2
Total connected devices: 2
Number of connections left: 6
Indications enabled
NEW CONNECTION
Role: Slave
Handle: #3
Total connected devices: 3
Number of connections left: 5
Restarted advertising
To see the gathered temperature values, perform the following steps:
Open the EFR Connect app on your mobile phone.
In the Bluetooth Browser, find the device with the name MSMMDT, and connect to it.
Find and open the Health Thermometer service.
Open the first Temperature Measurement characteristic and enable indications on it (by tapping the indications icon). Now, the new value should be displayed received from the first thermometer.
Do the same for the second Temperature Measurement characteristic.