Creating a Bluetooth LE Central with STM32WB

This tutorial shows how to create a basic Bluetooth LE Central (a device that enumerates and communicates to Bluetooth LE peripherals) using the STM32WB platform and VisualGDB.

We will show the functions involved in enumerating the peripherals and obtaining various information from them. If you are not familiar with the Bluetooth LE concepts, follow our Bluetooth LE Peripheral tutorial to get started.

Before you begin, ensure you have an STM32WB Nucleo kit (including the Nucleo board and the USB dongle) and install VisualGDB 5.4 or later.

  1. First we will create a Bluetooth LE peripheral project for the USB dongle included with the STM32WB-Nucleo kit. Start Visual Studio and open the VisualGDB Embedded Project Wizard:
  2. Select the name and location for your project and press “Create” to launch the VisualGDB’s wizard:
  3. On the first page of the wizard select “Create a new project -> Embedded binary -> MSBuild“:
  4. On the next page select the device you are targeting. The STM32WB-Nucleo USB Dongle uses the STM32WB55CGU6 device, so we select it in the list:
  5. In this tutorial the dongle will be running a Bluetooth LE Peripheral project (server) and the STM32WB-Nucleo board will run the Central project (client). Hence, select BLE_p2pServer  under STM32CubeMX Samples:
  6. Unlike the Nucleo board itself, the USB dongle does not contain an embedded ST-Link debugger, hence you will not be able to debug it with VisualGDB. Select “Debug Methods -> Built-in GDB Simulator” as a debug method (note that as of July 2019, the GDB simulator does not support STM32 devices):
  7. Press “Finish” to create the project. As we will be using the STM32Programmer tool to upload the built project into the USB dongle, we recommend changing the target extension to .elf so that the tool can detect the file type automatically:
  8. Build the project by pressing Ctrl-Shift-B:
  9. Before you upload the built project into the dongle, you need to program the wireless stack binary (stm32wb5x_BLE_Stack_fw.bin) into it. Download and run our Wireless Stack Updater tool to upload the wireless stack automatically. Once the stack is uploaded, leave the USB dongle in the bootloader mode:
  10. Install the STM32CubeProg tool from ST and run the following command line:
    STM32_Programmer_CLI.exe -c port=usb1 -d <full path to PeripheralProject.elf>

  11. Once the dongle is programmed, move the SW2 switch back to the default position and replug the dongle. Observe how the green LED begins blinking:
  12. You can use the Bluetooth LE Explorer app to verify that the dongle is advertising itself as a Bluetooth LE peripheral:
  13. Click on the device to view services and characteristics provided by it:
  14. You can read more about the STM32WB API used to register services and characteristics on the device side in our STM32WB Peripheral Tutorial. This tutorial will instead focus on the API for accessing those characteristics from a Bluetooth LE Central project. Select File->New->Project again, pick the VisualGDB Embedded Project Wizard and specify the location for the central project:
  15. As the project will run on the Nucleo board itself, select the STM32WB55RG device that is present on the board:
  16. On the sample page select the BLE_p2pClient sample:
  17. Plug in your Nucleo board via USB and let VisualGDB detect the ST-Link debug settings. Then press Finish to create the project:
  18. If you haven’t done this previously, program the stm32wb5x_BLE_Stack_fw.bin stack into the Nucleo board using our Wireless Stack Updater tool:
  19. Press F5 to build the Bluetooth LE Central project and begin debugging it:
  20. Once the project begins running on the Nucleo board, the green LED (LED2) will turn on:
  21. Replug the USB dongle into the USB port and confirm that its green LED begins blinking, indicating that the Bluetooth LE advertisement is in progress. Then press the leftmost button (SW1) on the Nucleo board. The blue LED will turn on indicating the the scan is in progress:Once the scan is complete, the green LED on both Nucleo board and the USB dongle will begin blinking. Now you can press the button on the dongle to control the LED on the Nucleo board and the SW1 button on the Nucleo to control the LED on the dongle.
  22. Now we will go through the STM32 functions involved in discovering and communicating with the BLE Peripheral running on the USB dongle. Set a breakpoint inside the HAL_GPIO_EXTI_Callback() function in app_entry.c and restart the program via the button in the GDB Session window. Then press the SW1 button again. Once the breakpoint triggers, step through the HAL_GPIO_EXTI_Callback() function and into APP_BLE_Key_Button1_Action():
  23. Note that the handler for the SW1 button does not immediately start calling the Bluetooth LE API. Instead, it enables the CFG_TASK_START_SCAN_ID task. You can use the “Find all references” function to find out that the task is implemented in the Scan_Request() function. STM32WB devices use the task scheduler to minimize power usage. Most of the time the CPU will be in the low power mode, only waking up to process a certain event. When the event arrives, the event handler typically wakes up a task that will do further processing. Tasks are created via the SCH_RegTask() function and are woken up by calling SCH_SetTask(). Set a breakpoint in Scan_Request() and continue the program to let the breakpoint trigger:
  24. The logic inside Scan_Request() is very simple – it calls aci_gap_start_general_discovery_proc() and immediately returns, letting the device go into the lower power mode until the Bluetooth LE logic receives some advertisement packets from BLE peripherals. Locate the line assigning 0x01 to the BleApplicationContext.DeviceServerFound field in app_ble.c, set a breakpoint there and wait for it to trigger:The breakpoint will trigger once the SVCCTL_App_Notification() function gets called with an event type of EVT_LE_META_EVENT and sub-event of EVT_LE_ADVERTISING_REPORT. The logic in SVCCTL_App_Notification() checks that the advertisement packet contains the CFG_DEV_ID_P2P_SERVER1 value,  remembers the BLE Peripheral address and sets the DeviceServerFound field.
  25. Use the “Find References” command to locate the code that checks the DeviceServerFound field later, set a breakpoint there and let it triggerThis code is located in the SVCCTL_App_Notification() function and gets invoked when the Bluetooth LE stack sends the EVT_VENDOR event with the EVT_BLUE_GAP_PROCEDURE_COMPLETE subevent.
  26. The code in SVCCTL_App_Notification() wakes up the Connect_Request() task and then the following events take place:
    1. Connect_Request() calls aci_gap_create_connection() to establish a connection to the peripheral.
    2. When the connection is created, the BLE stack sends the EVT_LE_META_EVENT/EVT_LE_CONN_COMPLETE event to
      SVCCTL_App_Notification().
    3. The handler for the EVT_LE_CONN_COMPLETE event calls aci_gatt_disc_all_primary_services() to enumerate all services of the peripheral.
    4. Once the services are enumerated, the BLE stack sends the EVT_BLUE_GATT_PROCEDURE_COMPLETE event to the Event_Handler() callback in p2p_client_app.c.
    5. The EVT_BLUE_GATT_PROCEDURE_COMPLETE handler wakes up the Update_Service() task.
    6. The Update_Service() task together with Event_Handler() implement a basic state machine. Each time a related Bluetooth LE event arrives, Event_Handler() updates the internal state in the aP2PClientContext[index].state field and wakes up Update_Service() that starts the next Bluetooth LE request. In this example, the following requests take place:

      1. aci_gatt_disc_all_char_of_service() is called to discover all characteristics of the discovered service.
      2. aci_gatt_disc_all_char_desc() is called to get the descriptors of the P2P_NOTIFY_CHAR_UUID characteristic.
      3. aci_gatt_write_char_desc() is called to subscribe to change notifications from the characteristic by writing to its CCCD descriptor.
    7. When the button on the USB dongle is pressed and it reports a change in the characteristic value, Gatt_Notification() is invoked on the Nucleo board to toggle the LED.

    You can find the code responsible for the LED toggling by setting a breakpoint in Gatt_Notification() and pressing the button on the USB dongle:

You can change the logic of the Bluetooth LE Central used in this example by adding more states to the state machine implemented in Update_Service() and Event_Handler(). I.e. initiate new Bluetooth LE operations from Update_Service() and update the state in the Event_Handler(). This will minimize the power consumption by the STM32WB device, as it will automatically enter the low power state as soon as it is done handling an event.