This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:iot-open:practical:hardware:sut:stm32:iot_6 [2024/05/03 08:20] – ktokarz | en:iot-open:practical:hardware:sut:stm32:iot_6 [2024/05/04 08:10] (current) – ktokarz | ||
---|---|---|---|
Line 13: | Line 13: | ||
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. | ||
< | < | ||
- | The Nucleo STM32WB55 development | + | The Nucleo STM32WB55 development |
</ | </ | ||
Line 48: | 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:// | 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:// | ||
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. | ||
- | < | + | < |
#define SERVICE_UUID | #define SERVICE_UUID | ||
#define CHARACTERISTIC_UUID " | #define CHARACTERISTIC_UUID " | ||
Line 63: | Line 63: | ||
The function creates and initialises the BLE device instance named "SUT STM BLE". It creates a service with the characteristic, | The function creates and initialises the BLE device instance named "SUT STM BLE". It creates a service with the characteristic, | ||
- | < | + | < |
void setup() { | void setup() { | ||
| | ||
Line 90: | Line 90: | ||
</ | </ | ||
- | In the loop function, we need to call a function waiting for the connection of the client. | + | In the loop function, we need to call a function waiting for the connection of the client |
- | < | + | < |
void loop() { | void loop() { | ||
- | // listen for BLE peripherals | + | // listen for BLE central device |
BLE.central(); | BLE.central(); | ||
} | } | ||
Line 103: | Line 103: | ||
=== Step 3 === | === Step 3 === | ||
- | While we have analysed the client' | + | While we have analysed the client' |
- | < | + | Add the libraries to the platformio.ini. |
+ | < | ||
+ | lib_deps = | ||
+ | stm32duino/ | ||
+ | arduino-libraries/ | ||
+ | </ | ||
+ | And import libraries into the cpp file. | ||
+ | <code c> | ||
#include " | #include " | ||
- | #include "BLEDevice.h" | + | #include "STM32duinoBLE.h" |
- | #include "Adafruit_LiquidCrystal.h" | + | #include "LiquidCrystal.h" |
</ | </ | ||
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. | ||
- | < | + | < |
// The remote service we wish to connect to. | // The remote service we wish to connect to. | ||
- | # | + | # |
// The characteristic of the remote service we are interested in. | // The characteristic of the remote service we are interested in. | ||
#define REMOTE_CHARACTERISTIC_UUID " | #define REMOTE_CHARACTERISTIC_UUID " | ||
</ | </ | ||
- | + | < | |
- | 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. |
- | < | + | </ |
+ | Some global | ||
+ | < | ||
// Variables | // Variables | ||
- | static boolean doConnect = false; | + | HCISharedMemTransportClass HCISharedMemTransport; |
- | static boolean connected = false; | + | BLELocalDevice BLEObj(& |
- | static boolean doScan | + | BLELocalDevice& |
- | static BLERemoteCharacteristic* pRemoteCharacteristic; | + | BLECharacteristic remoteCharacteristic; |
- | static BLEAdvertisedDevice* myDevice; | + | |
</ | </ | ||
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 |
- | #define LCD_D4 39 | + | |
- | #define LCD_D5 40 | + | |
- | #define LCD_D6 41 | + | |
- | #define LCD_D7 42 | + | |
- | static Adafruit_LiquidCrystal | + | |
</ | </ | ||
- | 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 | + | In the setup() function |
- | < | + | < |
- | // Scan for BLE servers and find the first one that advertises | + | void setup() { |
- | // the service we are looking for. | + | // initialise LCD |
- | class MyAdvertisedDeviceCallbacks: | + | lcd.begin(16, 2); |
- | | + | |
- | // We will print the asterix for every device found | + | |
- | lcd.print(" | + | |
- | + | BLE.begin(); | |
- | // We have found a device, let's see if it contains | + | |
- | if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) { | + | |
- | | + | |
- | | + | int ret = 1; |
- | + | do | |
- | // Create the instance of remote device | + | { |
- | | + | |
- | + | if (ret == 0) | |
- | // Pass information to other part of the program to connect to the device | + | { |
- | | + | |
- | | + | |
- | + | } | |
+ | } while(ret | ||
+ | } | ||
+ | </ | ||
+ | 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' | ||
- | } // Found our server | + | <code c> |
- | | + | void loop() { |
- | }; // MyAdvertisedDeviceCallbacks | + | // check if a peripheral has been discovered |
- | </ | + | |
- | 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) { |
- | < | + | 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, | lcd.setCursor(0, | ||
- | lcd.print(" | + | lcd.print(peripheral.localName()); |
- | lcd.setCursor(0,1); | + | |
- | connected = false; | + | |
- | } | + | |
- | };</ | + | |
- | The setup function initialises the Bluetooth, registers the advertising callback function, and starts the scan to look for nearby devices. | + | // stop scanning |
- | < | + | int ret = 1; |
- | void setup() | + | do |
- | // Initialise the LCD and print the welcome message | + | |
- | lcd.begin(16, 2); | + | ret = BLE.stopScan(); |
- | delay(1000); | + | |
- | lcd.setCursor(0,0); | + | { |
- | lcd.print("BLE Client | + | BLE.end(); |
+ | BLE.begin(); | ||
+ | } | ||
+ | } while(ret == 0); | ||
- | | + | |
- | | + | |
+ | } | ||
- | | + | |
- | | + | int ret = 1; |
- | + | do | |
- | // Register callback for incoming advertising | + | { |
- | | + | ret = BLE.scanForUuid(REMOTE_SERVICE_UUID); |
- | + | | |
- | // Start scan parameters with active scan mode | + | { |
- | pBLEScan-> | + | BLE.end(); |
- | | + | BLE.begin(); |
- | | + | } |
- | + | } while(ret == 0); | |
- | // Print the message on the LCD | + | |
- | lcd.setCursor(0,0); | + | |
- | lcd.print(" | + | |
- | | + | |
- | | + | |
- | // Start the scan for 30 seconds | + | |
- | pBLEScan-> | + | |
} | } | ||
</ | </ | ||
- | In the loop() | + | The function |
- | \\ | + | - Connects to the remote device. |
- | In a loop() function we will periodically read the characteristic | + | - Discovers attributes of the remote |
+ | - Retrieves the characteristic | ||
+ | - Checks if the remote device is properly | ||
+ | - Checks if the remote | ||
+ | - 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:: | + | |
- | } | + | |
- | } | + | |
| | ||
- | if (connected) { | + | |
- | lcd.setCursor(0,1); | + | |
| | ||
- | // Is the characteristic properly retrieved? | + | // discover peripheral attributes |
- | if(pRemoteCharacteristic != nullptr) | + | if (peripheral.discoverAttributes()) { |
- | + | ||
- | // Is the characteristic | + | // retrieve |
- | if(pRemoteCharacteristic-> | + | remoteCharacteristic = peripheral.characteristic(REMOTE_CHARACTERISTIC_UUID); |
- | | + | if (remoteCharacteristic) { |
- | | + | |
- | | + | // if the peripheral is connected display |
- | } | + | |
- | delay(1000); | + | |
- | } | + | |
- | </ | + | |
- | 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. | + | |
- | - 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. | + | |
- | - Informing the main program with the " | + | } // if (remoteCharacteristic.canRead()) |
- | + | | |
- | < | + | } // if (remoteCharacteristic) |
- | bool connectToServer() { | + | peripheral.disconnect(); |
- | | + | |
- | | + | |
- | + | ||
- | | + | |
- | | + | |
- | | + | |
- | // Obtain a reference to the service in the remote BLE server. | + | |
- | BLERemoteService* pRemoteService = pClient-> | + | |
- | + | ||
- | | + | |
- | pRemoteCharacteristic = pRemoteService-> | + | |
- | + | ||
- | connected = true; | + | |
- | return true; | + | |
} | } | ||
</ | </ | ||
Line 281: | Line 254: | ||
==== Result validation ==== | ==== Result validation ==== | ||
- | You should be able to observe messages describing | + | 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' |
===== FAQ ===== | ===== FAQ ===== | ||
Line 289: | Line 262: | ||
\\ | \\ | ||
**What is the maximum length of the data in the characteristics? | **What is the maximum length of the data in the characteristics? | ||
+ | \\ | ||
+ | **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. | ||
\\ | \\ | ||