Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
en:iot-open:practical:hardware:sut:stm32:iot_6 [2024/05/03 07:10] – [Suggested Readings and Knowledge Resources] ktokarzen:iot-open:practical:hardware:sut:stm32:iot_6 [2024/05/04 08:10] (current) ktokarz
Line 9: Line 9:
   * [[en:iot-open:hardware2:stm32|]]   * [[en:iot-open:hardware2:stm32|]]
   * [[en:iot-open:practical:hardware:sut:stm32|]]   * [[en:iot-open:practical:hardware:sut:stm32|]]
- 
  
 ===== Hands-on Lab Scenario ===== ===== Hands-on Lab Scenario =====
 This scenario is intended to be implemented using two BLE laboratory nodes. One of them is a server, while the second is a client. Here we will present the simplest implementation to be extended in further scenarios. This scenario is intended to be implemented using two BLE laboratory nodes. One of them is a server, while the second is a client. Here we will present the simplest implementation to be extended in further scenarios.
 +<note>
 +The Nucleo STM32WB55 development boards which are used in our STM laboratory don't have the BLE firmware flashed by default. If you would like to test this scenario on your own board please refer to the STM documentation on the flashing process ((https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Connectivity:STM32WB_FUS)). Read and follow the documentation carefully because of the danger of "bricking" your device.
 +</note>
  
 ==== Task to be implemented ==== ==== Task to be implemented ====
Line 29: Line 31:
 <code c> <code c>
 #include <Arduino.h> #include <Arduino.h>
-#include "BLEDevice.h+#include <STM32duinoBLE.h>
-#include "BLEUtils.h" +
-#include "BLEServer.h"+
 </code> </code>
-We need variables to hold the class pointers for advertising, the server instance, service, and characteristics.+We need variables for the BLE device class object and data transport class object.
 <code> <code>
-BLEAdvertising     *pAdvertising   //class for the advertising data +HCISharedMemTransportClass HCISharedMemTransport
-BLEServer          *pServer        //class for the BLE Server device +BLELocalDevice BLEObj(&HCISharedMemTransport)
-BLEService         *pService;        //class for the BLE Service in the Server +BLELocalDevice& BLE = BLEObj;
-BLECharacteristic  *pCharacteristic; //class for the characteristic +
-</code> +
-We will also need two boolean variables to control the restarting of the advertising. In case of establishing the connection from the remote client, the server device stops advertising, so it will not be searchable anymore. If we lose the connection with the client we need to restart advertising. +
-<code c> +
-bool deviceConnected = false; +
-bool advStarted true;+
 </code> </code>
  
Line 54: Line 48:
 While none of the standard UUIDs fits your device you need to define your own UUID. It must be 128-bit long and can be randomly generated with the tool available on the website https://www.uuidgenerator.net/((https://www.uuidgenerator.net/)). While none of the standard UUIDs fits your device you need to define your own UUID. It must be 128-bit long and can be randomly generated with the tool available on the website https://www.uuidgenerator.net/((https://www.uuidgenerator.net/)).
 The service UUID and characteristic UUID must differ. Although they can be completely different in many implementations the UUIDs of characteristics grouped under one service differ on one digit. The service UUID and characteristic UUID must differ. Although they can be completely different in many implementations the UUIDs of characteristics grouped under one service differ on one digit.
-<code>+<code c>
 #define SERVICE_UUID        "6e9b7b26-ca96-4774-b056-8ec5b759fd86" #define SERVICE_UUID        "6e9b7b26-ca96-4774-b056-8ec5b759fd86"
 #define CHARACTERISTIC_UUID "6e9b7b27-ca96-4774-b056-8ec5b759fd86" #define CHARACTERISTIC_UUID "6e9b7b27-ca96-4774-b056-8ec5b759fd86"
 </code> </code>
- +We create the service and characteristic objects with defined UUIDs
-Before we implement the setup() function, we have to create the callback function executed in case of connection and disconnection events. It allows us to control the advertising process with the deviceConnected flag. +
 <code c> <code c>
-class MyServerCallbacks: public BLEServerCallbacks { +// BLE Service - custam 128-bit UUID 
-    void onConnect(BLEServer* pServer+BLEService pService(SERVICE_UUID); 
-      deviceConnected = true; +
-    };+
  
-    void onDisconnect(BLEServer* pServer+// BLE Characteristic - custom 128-bit UUID, read and writable by central 
-      deviceConnected = false; +BLECharacteristic pCharacteristic(CHARACTERISTIC_UUID, BLERead | BLEWrite, 16);
-    } +
-};+
 </code> </code>
 +Data for reading or writing is organised using characteristics. In this example, we create one characteristic with reading and writing enabled, and the initial data as the "SUT STM" text. All is done in the setup() function.
 +The function creates and initialises the BLE device instance named "SUT STM BLE". It creates a service with the characteristic, sets the characteristic's initial value and finally starts advertising.
  
-The setup() function creates and initialises the BLE device instance with the name "SUT BLE device", and creates the server and sets the callback function. It also creates the service within the server.+<code c> 
 +void setup() 
 +   
 +  // begin initialization 
 +  BLE.begin();
  
-<code> +  // set advertised local name and service UUID: 
-// Initialise the BLE device +  BLE.setLocalName("SUT STM BLE"); 
-BLEDevice::init("SUT BLE device");  +  BLE.setAdvertisedService(pService);
  
-// Create BLE Server instance     +  // add the characteristic to the service 
-pServer = BLEDevice::createServer();+  pService.addCharacteristic(pCharacteristic);
  
-// Set callback function for handling server events     +  // add service 
-pServer->setCallbacks(new MyServerCallbacks());+  BLE.addService(pService);
  
-// Create the service in the server +  // set the initial value for the characeristic: 
-pService = pServer->createService(SERVICE_UUID);+  pCharacteristic.writeValue("SUT STM");
  
-</code> +  // start advertising 
-Data for reading or writing is organised using characteristics. In this example, we create one characteristic with reading and writing enabled, and the initial data as the "BLE onboard" text. +  BLE.advertise(); 
-<code> +}
-// Create the characteristic in the service +
-pCharacteristic = pService->createCharacteristic( +
-                                         CHARACTERISTIC_UUID, +
-                                         BLECharacteristic::PROPERTY_READ | +
-                                         BLECharacteristic::PROPERTY_WRITE +
-                                       ); +
-                                        +
-// Set the initial value of the characteristic. +
-// We will be able to read it by client +
-pCharacteristic->setValue("BLE onboard"); +
-</code> +
-In the advertising packet, we can add the service UUID. +
-<code> +
-// Get the pointer to the advertising object +
-pAdvertising = BLEDevice::getAdvertising(); +
- +
-// Add the service UUID to the advertising +
-pAdvertising->addServiceUUID(SERVICE_UUID); +
- +
-// Enable reading extended information with scan response packet +
-pAdvertising->setScanResponse(true);+
 </code> </code>
 <note> <note>
-Due to the limited size of the advertisement packet, not all service UUIDs can be broadcast. Even more, service UUID does not have to appear in the advertising frame, but its presence makes it possible to scan nearby devices by their functionality, not by names only.+Although it is possible to have many services in one device, due to the limited size of the advertisement packet, not all service UUIDs can be broadcast. Even more, service UUID does not have to appear in the advertising frame, but its presence makes it possible to scan nearby devices by their functionality, not by names only.
 </note> </note>
-After preparing all the elements we can close the setup() function by starting the service and beginning advertising. 
-<code> 
-// Starting the service and advertising process 
-pService->start(); 
-pAdvertising->start(); 
-</code> 
-In the loop function, we need to handle the restarting of advertising in case of disconnection of the client. 
-<code> 
-void loop(){ 
-    if (!deviceConnected && !advStarted) { 
-        pServer->startAdvertising(); // restart advertising 
-        advStarted = true; 
-    } 
  
-    if (deviceConnected){ +In the loop function, we need to call a function waiting for the connection of the client (central device). 
-        advStarted = false; +<code c> 
-    } +void loop() { 
-    delay(500); +  // listen for BLE central device to connect: 
-};+  BLE.central(); 
 +}
 </code> </code>
  
Line 142: Line 103:
  
 === Step 3 === === Step 3 ===
-While we have analysed the client's behaviour we can start implementation. Let's begin with the libraries needed.  +While we have analysed the client's behaviour we can start implementation. Let's begin with the libraries needed
-<code>+Add the libraries to the platformio.ini.  
 +<code c> 
 +lib_deps =  
 +  stm32duino/STM32duinoBLE @ 1.2.6 
 +  arduino-libraries/LiquidCrystal@^1.0.7 
 +</code>  
 +And import libraries into the cpp file. 
 +<code c>
 #include "Arduino.h" #include "Arduino.h"
-#include "BLEDevice.h" +#include "STM32duinoBLE.h" 
-#include "Adafruit_LiquidCrystal.h"+#include "LiquidCrystal.h"
 </code> </code>
 Next, we define the UUIDs for remote service and a characteristic. Notice they must match the ones defined in the server. Next, we define the UUIDs for remote service and a characteristic. Notice they must match the ones defined in the server.
-<code>+<code c>
 // The remote service we wish to connect to. // The remote service we wish to connect to.
-#define SERVICE_UUID "6e9b7b26-ca96-4774-b056-8ec5b759fd86"+#define REMOTE_SERVICE_UUID "6e9b7b26-ca96-4774-b056-8ec5b759fd86"
 // The characteristic of the remote service we are interested in. // The characteristic of the remote service we are interested in.
 #define REMOTE_CHARACTERISTIC_UUID "6e9b7b27-ca96-4774-b056-8ec5b759fd86" #define REMOTE_CHARACTERISTIC_UUID "6e9b7b27-ca96-4774-b056-8ec5b759fd86"
 </code> </code>
- +<note> 
-Some global variables will be needed for our software. +We intentionally specified here the same UUIDs as in ESP32 BLE scenarios. If both platforms are physically available in the same laboratory, you can try to connect different hardware platforms with BLE. 
-<code>+</note> 
 +Some global class objects and variables will be needed for our software. 
 +<code c>
 // Variables // Variables
-static boolean doConnect = false+HCISharedMemTransportClass HCISharedMemTransport
-static boolean connected = false+BLELocalDevice BLEObj(&HCISharedMemTransport)
-static boolean doScan false; +BLELocalDevice& BLE BLEObj
-static BLERemoteCharacteristic* pRemoteCharacteristic+BLECharacteristic remoteCharacteristic;
-static BLEAdvertisedDevice* myDevice;+
 </code> </code>
 We need to add and configure the LCD to be able to observe the results. We need to add and configure the LCD to be able to observe the results.
 <code c> <code c>
-// LCD display pins and class declaration +// LCD class (Constructor uses STM port numbering) 
-#define LCD_RS 2 +const int rs = PC5, en = PB11, d4 = PB12, d5 = PB13, d6 = PB14, d7 = PB15; 
-#define LCD_ENABLE 1 +LiquidCrystal lcd(rsend4d5d6d7);
-#define LCD_D4 39 +
-#define LCD_D5 40 +
-#define LCD_D6 41 +
-#define LCD_D7 42 +
-static Adafruit_LiquidCrystal lcd(LCD_RSLCD_ENABLELCD_D4LCD_D5LCD_D6LCD_D7);+
 </code> </code>
  
-Two callback functions will be defined. The first callback is for advertising. It is called whenever the advertising frame is received, no matter which device sends it. Inside the callback, we search for the device nearby which presents the service we would like to use. If we find one, we can create the instance of the remote device class and pass the signal to the main program to establish the connection.+In the setup() function we initialise LCD and Bluetooth and start scanning for the device having the service UUID we want to connect.
  
-<code> +<code c
-// Scan for BLE servers and find the first one that advertises  +void setup() { 
-// the service we are looking for. +  // initialise LCD 
-class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { +  lcd.begin(16, 2); 
-  void onResult(BLEAdvertisedDevice advertisedDevice) { +  lcd.clear(); 
-    // We will print the asterix for every device found +   
-    lcd.print("*"); +  // initialise the BLE hardware 
-     +  BLE.begin();
-    // We have found a device, let's see if it contains the service we are looking for. +
-    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {+
  
-      // Our device has the service we need. We can stop scanning. +  // start scanning for peripherals 
-      BLEDevice::getScan()->stop()+  int ret = 1
-       +  do 
-      // Create the instance of remote device +  { 
-      myDevice new BLEAdvertisedDevice(advertisedDevice); +    ret BLE.scanForUuid(REMOTE_SERVICE_UUID); 
-       +    if (ret == 0) 
-      // Pass information to other part of the program to connect to the device +    { 
-      doConnect = true+      BLE.end()
-      doScan true+      BLE.begin(); 
-      +    } 
 +  } while(ret == 0)
 +
 +</code>
  
 +In a loop() function we check if a peripheral with UUID we are interested in was discovered. If so we check its name. If the remote device's name is as we expect we print its name, stop scanning, and call the function which reads and displays the value of the characteristic. While the function returns, we restart scanning.
  
-    } // Found our server +<code c> 
-  // onResult +void loop() { 
-}// MyAdvertisedDeviceCallbacks +  // check if a peripheral has been discovered 
-</code>+  BLEDevice peripheral = BLE.available();
  
-The second callback class helps to inform the other parts of the program about the connection close. It will additionally inform us about the current state of the program. +  if (peripheral) { 
-<code> +    if (peripheral.localName() == "SUT STM BLE"{
-class MyClientCallback : public BLEClientCallbacks { +
-  void onConnect(BLEClient* pclient) { +
-      lcd.setCursor(0,0)+
-      lcd.print("Connected       ")+
-  }+
  
-  void onDisconnect(BLEClient* pclient) {+      // display remote name  
       lcd.setCursor(0,0);       lcd.setCursor(0,0);
-      lcd.print("Disconnected    "); +      lcd.print(peripheral.localName());
-      lcd.setCursor(0,1); +
-    connected = false; +
-  } +
-};</code>+
  
-The setup function initialises the Bluetooth, registers the advertising callback function, and starts the scan to look for nearby devices. +      // stop scanning 
-<code> +      int ret = 1; 
-void setup() +      do 
-  // Initialise the LCD and print the welcome message +      
-  lcd.begin(16, 2); +        ret = BLE.stopScan(); 
-  delay(1000); +        if (ret == 0
-  lcd.setCursor(0,0); +        { 
-  lcd.print("BLE Client      ");+          BLE.end(); 
 +          BLE.begin(); 
 +        } 
 +      } while(ret == 0);
  
-  // Initialise the Bluetooth +      // display the characteristic value 
-  BLEDevice::init("");+      display_characteristic(peripheral); 
 +    }
  
-  // Retrieve the pointer to the scan module +    // peripheral disconnected, start scanning again 
-  BLEScan* pBLEScan BLEDevice::getScan()+    int ret 1
-   +    do 
-  // Register callback for incoming advertising +    { 
-  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); +      ret = BLE.scanForUuid(REMOTE_SERVICE_UUID); 
-   +      if (ret == 0
-  // Start scan parameters with active scan mode +      { 
-  pBLEScan->setInterval(1349); +        BLE.end(); 
-  pBLEScan->setWindow(449); +        BLE.begin(); 
-  pBLEScan->setActiveScan(true); +      } 
-   +    } while(ret == 0); 
-  // Print the message on the LCD +  }
-  lcd.setCursor(0,0); +
-  lcd.print("Scanning        "); +
-  lcd.setCursor(0,1); +
-   +
-  // Start the scan for 30 seconds +
-  pBLEScan->start(30, false);+
 } }
 </code> </code>
  
-In the loop() function, we wait for the information that the server with the service UUID we were interested in was foundIt is signalled with the use of "doConnect" flagIf it's true we can connect to this server. Once we are connected the "connected" flag is set to be true in the connectToServer() function. We will show this function in a while+The function reads the characteristic value in some steps: 
-\\ +  - Connects to the remote device. 
-In a loop() function we will periodically read the characteristic valueIt is a good practice to check if the characteristic is properly retrieved and readable before reading the value.+  - Discovers attributes of the remote service. 
 +  - Retrieves the characteristic with specified UUID. 
 +  - Checks if the remote device is properly connected. 
 +  - Checks if the remote characteristic can be read. 
 +  - Reads the text and displays it on LCD. 
 +  - Disconnects from the remote server. 
 <code c> <code c>
-void loop() { +void display_characteristic(BLEDevice peripheral) {
-  if (doConnect == true) { +
-    // Establish the connection to the server  +
-    connectToServer();      +
-    doConnect = false; +
-  } +
- +
-  if (!connected) { +
-    if(doScan){ +
-      // Start scan again after disconnect +
-      BLEDevice::getScan()->start(0);   +
-    } +
-  }+
      
-  if (connected) { +  // connect to the peripheral 
-    lcd.setCursor(0,1);+  if (peripheral.connect()) {
          
-    // Is the characteristic properly retrieved? +    // discover peripheral attributes 
-    if(pRemoteCharacteristic != nullptr+    if (peripheral.discoverAttributes()) { 
-     +           
-      // Is the characteristic readable? +      // retrieve the remote characteristic to read 
-      if(pRemoteCharacteristic->canRead()+      remoteCharacteristic = peripheral.characteristic(REMOTE_CHARACTERISTIC_UUID); 
-       +      if (remoteCharacteristic
-        // We can safely read the value +    
-        lcd.print(pRemoteCharacteristic->readValue().c_str())+      // if the peripheral is connected display the value 
-  } +        if (peripheral.connected()) {
-  delay(1000); // Delay a second between loops. +
-}  +
-</code>+
  
-The connection to the server is executed in some steps: +          // check if the characteristic can be read 
-  - Creation of the client object+          if (remoteCharacteristic.canRead()){ 
-  - Setting the callback class for handling disconnection. +            char char_text[16]; 
-  - Connection to the remote device+            remoteCharacteristic.readValue(char_text,16); 
-  - Setting parameters of the connection+            int i=remoteCharacteristic.valueLength(); 
-  - Getting the reference to the service. +            lcd.setCursor(0,1); 
-  - Getting the reference to the characteristic. +            lcd.write(char_text,i); 
-  - Informing the main program with the "connected" flag. +          } // if (remoteCharacteristic.canRead()) 
- +        // if (peripheral.connected()) 
-<code> +      } // if (remoteCharacteristic) 
-bool connectToServer() { +      peripheral.disconnect(); 
-    BLEClient*  pClient  = BLEDevice::createClient(); +    // if (peripheral.discoverAttributes()) 
-    pClient->setClientCallbacks(new MyClientCallback()); +  // if (peripheral.connect())
- +
-    // Connect to the remote BLE Server+
-    pClient->connect(myDevice);   +
-     +
-    // Obtain a reference to the service in the remote BLE server. +
-    BLERemoteService* pRemoteService = pClient->getService(BLEUUID(SERVICE_UUID)); +
-     +
-    // Obtain a reference to the characteristic of the chosen service. +
-    pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(REMOTE_CHARACTERISTIC_UUID))+
-     +
-    connected = true; +
-    return true;+
 } }
 </code> </code>
Line 320: Line 254:
  
 ==== Result validation ==== ==== Result validation ====
-You should be able to observe messages describing the process of searching for the server on the first line of LCD and during scanning asterisks appearing on the second line. After establishing the connection, the characteristic value should appear on the display's second line.+You should be able to read the name of the remote device in the first line of the LCD. After establishing the connection, the characteristic value should appear on the display's second line.
 ===== FAQ ===== ===== FAQ =====
  
Line 328: Line 262:
 \\ \\
 **What is the maximum length of the data in the characteristics?**: The maximum length of the data in the characteristics in the BLE specification is 512 bytes. **What is the maximum length of the data in the characteristics?**: The maximum length of the data in the characteristics in the BLE specification is 512 bytes.
 +\\
 +**Can I send data between SoCs coming from different vendors?**: That's what the specifications of the network protocols are defined for. If devices are in their transmission range (i.e. in one physical laboratory site) you can implement a BLE server on one type of SoC and a BLE client on another.
 \\ \\
  
en/iot-open/practical/hardware/sut/stm32/iot_6.1714720240.txt.gz · Last modified: 2024/05/03 07:10 by ktokarz
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0