STM32WB Bluetooth® LE – Arduino Application

1. STM32WB - BLE P2P Server and Arduino

This is a description on how to create the proprietary application P2P Server thanks to the Arduino IDE.

Chapter 7.4 of the following application note describes the P2P Server application specification:

  • AN5289-Building wireless applications with STM32WB Series microcontrollers[1]

1.1. Arduino configuration for P-NUCLEO-WB55

  • Add the support of STM32 MCU in Arduino IDE[2]:
    • Arduino_Core_STM32[3]
    • STM32 boards support to Arduino[4]
  • Include STM32duinoBLE library:
    • STM32duinoBLE[5]
      • From the "Tools > Manage Libraries..." menu, select the STM32duinoBLE library:
STM32duinoBLE library
Connectivity STM32duinoBLE lib.png



  • Configuring IDE:
    • Connect the Nucleo-64 WB55 board to the computer USB port.
    • Launch the Arduino software.
    • Select the Nucleo-64 WB55 board in two steps:
      • From the "Tools > Board" menu, select the STM32 boards groups: Nucleo-64
      • Then from the "Tools > Board part number" menu, select the P-NUCLEO WB55RG.
Arduino Nucleo-64 WB55 configuration
Connectivity Arduino WB55 Configuration.png

1.2. BLE stack configuration for P-NUCLEO-WB55

BLE stack configuration for P-NUCLEO-WB55:

  • For release v1.12.0 and previous use full stack (stm32wb5x_BLE_Stack_full_fw.bin)
  • For release v1.13.0 and next use HCI layer stack (stm32wb5x_BLE_HCILayer_fw.bin) and do the following modification in stm32BLEDuino library:
    • In HCISharedMemTransport.cpp file comment the following lines:
HCISharedMemTransport.cpp modification
Connectivity STM32duinoBLE lib init.png


1.3. Sketch STM32WB_P2P_Server.ino

The following Arduino's Sketch demonstrates the P2P Server application including debug traces on the UART hyperterminal.

  • Compile and upload STM32WB_P2P_Server.ino
/**
******************************************************************************
* @file    STM32WB_P2P_Server.ino
* @author  WBL BLE Application Team
* @brief   Arduino STM32WB P2P Server Application (Custom STM)
******************************************************************************
* @attention
* This example creates a P2P server Application
* P2P service                     - 0000FE40-cc7a-482a-984a-7f2ed5b3e58f
* Led control Characteristic      - 0000FE41-8e22-4541-9d4c-21edae82ed19 (Read|WritewithoutResponse)
*                                     Byte 0 - Board Selection
*                                     Byte 1 - LED Status (1 : ON; 0 : OFF)
* Button control Characteristic   - 0000FE42-8e22-4541-9d4c-21edae82ed19 (Notify)
*                                     Byte 0 - Board Selection
*                                     Byte 1 - LED Status (1 : ON; 0 : OFF)
* To be supported by Smart Phone application ST BLE Sensor, the advertsing data shall contain the AD Element Manufacturer as following:
* Manufacturer_data:
*   0x01,                   // SKD version 
*   0x83,                   // CFG_DEV_ID_P2P_SERVER1 
*   0x00,                   // GROUP A Feature  
*   0x00,                   // GROUP A Feature 
*   0x00,                   // GROUP B Feature 
*   0x00,                   // GROUP B Feature 
* };
*
* The two characteristics are used :
*  -- to control (Write) the LED1 of Nucleo64-WB55 board LED 
*  -- or to Notify the remote device by pushing SW1 f Nucleo68-WB55 board.
*  
*  The circuit:
*  - Nucleo68-WB55
*  You can use a generic BLE central app, like ST BLE sensor or ST BLE Toolbox, to interact with the services and characteristics.
*  created in this sketch.
*
*  This example code is in the public domain.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include <STM32duinoBLE.h>

/* Private typedef -----------------------------------------------------------*/
typedef struct __attribute__((packed)) {
    byte boardSelection;
    byte ledStatus;
} P2P_STM32WB_led_t;
P2P_STM32WB_led_t ledControl;

typedef struct __attribute__((packed)) {
    byte boardSelection;
    byte ButtonStatus;
} P2P_STM32WB_button_t;
P2P_STM32WB_button_t buttonInfo;

/* Private variables ---------------------------------------------------------*/
const int buttonPin = PC4; // set buttonPin to digital pin PC4
const int ledPin = LED_BUILTIN; // set ledPin to on-board LED
const int buttonPushed = 1;
const int boardWB_1 = 1;

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 1;     // previous state of the button

/* Private variables ---------------------------------------------------------*/
/* UUID for P2P Service-------------------------------------------------------*/
BLEService P2PService("0000FE40-cc7a-482a-984a-7f2ed5b3e58f"); 

/* create switch characteristic and allow remote device to read and write -----*/
BLECharacteristic ledCharacteristic("0000FE41-8e22-4541-9d4c-21edae82ed19", BLERead | BLEWriteWithoutResponse,sizeof(ledControl),true);

/* create button characteristic and allow remote device to get notifications --*/
BLECharacteristic buttonCharacteristic("0000FE42-8e22-4541-9d4c-21edae82ed19", BLENotify,sizeof(buttonInfo),true);

/* Manufacturer Advertising Data element structure ----------------------------*/
static const uint8_t manufacturer_data[6] = {
    0x01, // BlueST Protocol - SDK Version
    0x83, // Device ID - STM32WB P2P Server 
    0x00, // GROUP A Feature
    0x00, // GROUP A Feature
    0x00, // GROUP B Feature
    0x00, // GROUP B Feature
};

HCISharedMemTransportClass HCISharedMemTransport;
BLELocalDevice BLEObj(&HCISharedMemTransport);
BLELocalDevice& BLE = BLEObj;

/**
* @brief  Connection Event Handler
* @param  BLE Central Device
* @retval None
*/
void blePeripheralConnectHandler(BLEDevice central) {
  // central connected event handler
  Serial.print("==> Connection established with central: ");
  Serial.print(central.address());
    if (BLE.connected()) {
      Serial.print(" -- RSSI = ");
      Serial.println(BLE.rssi());
    }
}

/**
* @brief  Disconnection Event Handler
* @param  BLE Central Device
* @retval None
*/
void blePeripheralDisconnectHandler(BLEDevice central) {
  // central disconnected event handler
  Serial.print("==> Disconnection event with central: ");
  Serial.println(central.address());
}

/**
* @brief  Event Handler when Characteristic Written 
* @param  BLE Central Device
* @param  Characteritic
* @retval None
*/
void LedCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
  byte data[sizeof(ledControl)];
  // central wrote new value to characteristic, update LED
  if (ledCharacteristic.written()) {
    Serial.println("==>Led Characteritics written:");
    Serial.print("Data Received: ");
    ledCharacteristic.readValue(data, sizeof(ledControl));
    Serial.print(ledCharacteristic.valueLength());
    Serial.print(" ");
    Serial.print("bytes -- ");
    for (int i = 0; i < sizeof(ledControl); i++) {
      Serial.print("0x");
      Serial.print(data[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
    ledControl.boardSelection=data[0];
    ledControl.ledStatus=data[1];
    Serial.print("Board Selected:");
    Serial.println(ledControl.boardSelection);
    Serial.print("Value:");
    Serial.println(ledControl.ledStatus);

    if(ledControl.ledStatus){
      Serial.println("==>LED1 ON");
      digitalWrite(ledPin, HIGH);
    }
    if(!ledControl.ledStatus){
      Serial.println("==>LED1 OFF");
      digitalWrite(ledPin, LOW);
    }
  }
}  

/**
* @brief  Arduino main Setup 
* @param  None
* @retval None
*/
void setup() {
  Serial.begin(115200);
  while (!Serial);

  pinMode(ledPin, OUTPUT); // use the LED as an output
  pinMode(buttonPin, INPUT_PULLUP); // use button pin as an input

  //Project P2P server
  Serial.println("======================================");
  Serial.println("== STM32WB - P2P Server Application ==");
  Serial.println("======================================");
  
  // begin initialization
  if (!BLE.begin()) {
    Serial.println("==>starting BLE failed!");

    while (1);
  }
  
  //Set the device name in the built in device name characteristic.
  BLE.setDeviceName("STM32WB-Arduino");
  //set the local name peripheral advertises
  Serial.println("==> Advertising Name - P2P_Server");
  BLE.setLocalName("P2P_Server");
  //Set the manufacturer data value used when advertising.
  BLE.setManufacturerData(manufacturer_data, sizeof(manufacturer_data));
  
  //Set the advertising interval in units of 0.625 ms. 
  //Defaults to 100ms (160 * 0.625 ms) if not provided. 
  BLE.setAdvertisingInterval(160); // 160 * 0.625 ms
   
  // set the UUID for the service this peripheral advertises:
  //BLE.setAdvertisedService(P2PService);

  // add the characteristics to the service
  P2PService.addCharacteristic(ledCharacteristic);
  P2PService.addCharacteristic(buttonCharacteristic);
  // assign event handlers for characteristic
  ledCharacteristic.setEventHandler(BLEWritten, LedCharacteristicWritten);

  // add the service
  BLE.addService(P2PService);

  //configure data to Notify when SW1 pushed
  buttonInfo.boardSelection = boardWB_1;
  buttonInfo.ButtonStatus=buttonPushed;
  
  // start advertising
  BLE.advertise();

  String address = BLE.address();
  Serial.print("==>Local address is: ");
  Serial.println(address);

  Serial.println("==>Advertising Started, waiting for connections...");

  // assign event handlers for connected, disconnected to peripheral
  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
}

/**
* @brief  Arduino Loop 
* @param  None
* @retval None
*/
void loop() {
  // poll for BLE events
  BLE.poll();

  // read the SW1 Button input pin:
  buttonState = digitalRead(buttonPin);
  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      //Serial.println("on");
      Serial.print("==> Number of time button is pushes: ");
      Serial.println(buttonPushCounter);
      
      //check if connection is established by Central device
      if(BLE.central()){
        //check if Notification is enabled
        if (buttonCharacteristic.subscribed()) {
          Serial.println("buttonCharacteristic is subscribed:");
          Serial.println("==>Notification of Button Pushed is sent");
          buttonCharacteristic.writeValue((byte*) &buttonInfo, sizeof(buttonInfo));
        } else {
           Serial.println("buttonCharacteristic is NOT subscribed:");
           Serial.println("==>Notification is NOT sent");
        }
       }
    } else {
      // if the current state is LOW then the button went from on to off:
      //Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
}
/************************************ STMicroelectronics *****END OF FILE****/

1.4. Debug traces

At startup, the HyperTerminal displays the following traces

Startup Debug Trace
Connectivity Arduino WB55 Debug.png

2. Smart Phone application - ST BLE Sensor

  • Download ST BLE Sensor or ST BLE ToolBox application on the smartphone.
  • Scan and connect to P2P_Server
  • Toggle SmartPhone LED or push SW1 button of the Nucleo-64 WB55 board.
ST BLE Sensor Scan ST BLE Sensor Connect
Connectivity Arduino ST BLE Sensor Adv.jpg
Connectivity Arduino ST BLE Sensor connect.jpg
  • Debug trace displays all the sequences at the application level
Arduino Nucleo-64 WB55 traces
Connectivity Arduino WB55 Trace.png

3. References