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_7 [2024/05/04 09:20] – ktokarz | en:iot-open:practical:hardware:sut:stm32:iot_7 [2024/05/04 11:03] (current) – [STM_IoT_7: BLE Communication with notifications] ktokarz | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== STM_IoT_7: BLE Communication with notifications | + | ====== STM_IoT_7: BLE Communication with indications |
This scenario presents how to extend the Bluetooth Low Energy server and client devices with a notification or indication mechanism for sending data automatically. If enabled, notifications or indications are sent at any time while the data in the server is updated. A difference between them is that a notification is an unacknowledged message while an indication is an acknowledged message. While one of them is enabled by the client, the server decides on the time of the message sent. | This scenario presents how to extend the Bluetooth Low Energy server and client devices with a notification or indication mechanism for sending data automatically. If enabled, notifications or indications are sent at any time while the data in the server is updated. A difference between them is that a notification is an unacknowledged message while an indication is an acknowledged message. While one of them is enabled by the client, the server decides on the time of the message sent. | ||
Line 17: | Line 17: | ||
==== Task to be implemented ==== | ==== Task to be implemented ==== | ||
- | **Task 1.** Implement a program that operates as the BLE server which advertises itself and allows us to connect to. We should be able to subscribe to notifications | + | **Task 1.** Implement a program that operates as the BLE server which advertises itself and allows us to connect to. We should be able to subscribe to indications |
\\ | \\ | ||
- | **Task 2.** Implement a client device, capable of subscribing to the characteristic and reading the exemplary data from a server with a notification | + | **Task 2.** Implement a client device, capable of subscribing to the characteristic and reading the exemplary data from a server with a indication |
==== Start ==== | ==== Start ==== | ||
Line 25: | Line 25: | ||
==== Steps ==== | ==== Steps ==== | ||
- | We will pass through the lab in a few steps. We will add the second characteristic to the example from lab [[en: | + | We will pass through the lab in a few steps. We will add the second characteristic to the example from lab [[en: |
=== Step 1 === | === Step 1 === | ||
We start with the program written during the laboratory [[en: | We start with the program written during the laboratory [[en: | ||
<code c> | <code c> | ||
- | # | + | # |
// BLE Characteristic - custom 128-bit UUID, read and notify enabled | // BLE Characteristic - custom 128-bit UUID, read and notify enabled | ||
- | BLEByteCharacteristic | + | BLEByteCharacteristic |
</ | </ | ||
Line 38: | Line 39: | ||
< | < | ||
// add the characteristic to the service | // add the characteristic to the service | ||
- | pService.addCharacteristic(pNotifyCharacteristic); | + | pService.addCharacteristic(pIndicateCharacteristic); |
// set the initial value for the characteristic: | // set the initial value for the characteristic: | ||
- | pNotifyCharacteristic.setValue(1); | + | pIndicateCharacteristic.setValue(1); |
</ | </ | ||
Line 47: | Line 48: | ||
=== Step 2 === | === Step 2 === | ||
- | At this step, we implement periodical data sending. We add the counter variable, an integer for holding the value incremented every loop pass. | + | At this step, we implement periodical data sending. We add the counter variable, an integer for holding the value incremented every loop pass. We will increment this counter with use of millis() function to do not block the loop() with delay(); |
<code c> | <code c> | ||
int counter = 1; | int counter = 1; | ||
+ | int delay_millis; | ||
</ | </ | ||
In the main loop() we implement the incrementation of the counter and updating of the characteristic. | In the main loop() we implement the incrementation of the counter and updating of the characteristic. | ||
<code c> | <code c> | ||
- | pCharacteristic.setValue(counter); | + | void loop() { |
- | counter++; | + | // listen for BLE peripherals to connect: |
- | delay(1000); | + | BLE.central(); |
+ | if (millis() > delay_millis +1000){ | ||
+ | delay_millis = millis(); | ||
+ | pIndicateCharacteristic.setValue(counter); | ||
+ | counter++; | ||
+ | } | ||
+ | } | ||
</ | </ | ||
Line 63: | Line 72: | ||
{{ en: | {{ en: | ||
- | === Step 3 === | + | === Step 4 === |
- | While we have analysed the client' | + | While we have analysed the client' |
- | Add the libraries to the platformio.ini. | + | |
- | <code c> | + | |
- | lib_deps = | + | |
- | stm32duino/ | + | |
- | arduino-libraries/ | + | |
- | </ | + | |
- | And import libraries into the cpp file. | + | |
- | <code c> | + | |
- | #include " | + | |
- | #include " | + | |
- | #include " | + | |
- | </ | + | |
- | Next, we define the UUIDs for remote | + | |
<code c> | <code c> | ||
- | // The remote service we wish to connect to. | + | # |
- | # | + | |
- | // The characteristic of the remote service we are interested in. | + | |
- | #define REMOTE_CHARACTERISTIC_UUID " | + | |
</ | </ | ||
< | < | ||
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. | 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 class objects and variables will be needed for our software. | ||
- | <code c> | ||
- | // Variables | ||
- | HCISharedMemTransportClass HCISharedMemTransport; | ||
- | BLELocalDevice BLEObj(& | ||
- | BLELocalDevice& | ||
- | BLECharacteristic remoteCharacteristic; | ||
- | </ | ||
- | We need to add and configure the LCD to be able to observe the results. | ||
- | <code c> | ||
- | // LCD class (Constructor uses STM port numbering) | ||
- | const int rs = PC5, en = PB11, d4 = PB12, d5 = PB13, d6 = PB14, d7 = PB15; | ||
- | LiquidCrystal lcd(rs, en, d4, d5, d6, d7); | ||
- | </ | ||
- | In the setup() function we initialise LCD and Bluetooth and start scanning for the device having the service UUID we want to connect. | + | 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' |
- | + | <note> | |
- | <code c> | + | At the time of writing this book the mechanism |
- | void setup() { | + | </note> |
- | // initialise LCD | + | We will present the full code of the function to subscribe and receive the indications. |
- | lcd.begin(16, | + | |
- | lcd.clear(); | + | |
- | + | ||
- | // initialise the BLE hardware | + | |
- | BLE.begin(); | + | |
- | + | ||
- | // start scanning for peripherals | + | |
- | int ret = 1; | + | |
- | do | + | |
- | { | + | |
- | ret = BLE.scanForUuid(REMOTE_SERVICE_UUID); | + | |
- | if (ret == 0) | + | |
- | { | + | |
- | BLE.end(); | + | |
- | BLE.begin(); | + | |
- | } | + | |
- | } while(ret == 0); | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | 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' | + | |
- | + | ||
- | <code c> | + | |
- | void loop() { | + | |
- | // check if a peripheral has been discovered | + | |
- | BLEDevice peripheral = BLE.available(); | + | |
- | + | ||
- | if (peripheral) { | + | |
- | if (peripheral.localName() == "SUT STM BLE") { | + | |
- | + | ||
- | // display remote name | + | |
- | lcd.setCursor(0, | + | |
- | lcd.print(peripheral.localName()); | + | |
- | + | ||
- | // stop scanning | + | |
- | int ret = 1; | + | |
- | do | + | |
- | { | + | |
- | ret = BLE.stopScan(); | + | |
- | if (ret == 0) | + | |
- | { | + | |
- | BLE.end(); | + | |
- | BLE.begin(); | + | |
- | } | + | |
- | } while(ret == 0); | + | |
- | + | ||
- | // display | + | |
- | display_characteristic(peripheral); | + | |
- | } | + | |
- | + | ||
- | // peripheral disconnected, | + | |
- | int ret = 1; | + | |
- | do | + | |
- | { | + | |
- | ret = BLE.scanForUuid(REMOTE_SERVICE_UUID); | + | |
- | if (ret == 0) | + | |
- | { | + | |
- | BLE.end(); | + | |
- | BLE.begin(); | + | |
- | } | + | |
- | } while(ret == 0); | + | |
- | } | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | The function reads the characteristic value in some steps: | + | |
- Connects to the remote device. | - Connects to the remote device. | ||
- Discovers attributes of the remote service. | - Discovers attributes of the remote service. | ||
- Retrieves the characteristic with specified UUID. | - Retrieves the characteristic with specified UUID. | ||
- Checks if the remote device is properly connected. | - Checks if the remote device is properly connected. | ||
- | - Checks if the remote characteristic can be read. | + | - Checks if the remote characteristic can be subscribed. |
- | - Reads the text and displays it on LCD. | + | - Subscribes to the characteristic with indication mechanism. |
- | - Disconnects from the remote server. | + | - Waits for the characteristic value update. |
+ | - Reads the value and displays it on LCD. | ||
<code c> | <code c> | ||
Line 186: | Line 100: | ||
// connect to the peripheral | // connect to the peripheral | ||
if (peripheral.connect()) { | if (peripheral.connect()) { | ||
- | | + | |
// discover peripheral attributes | // discover peripheral attributes | ||
if (peripheral.discoverAttributes()) { | if (peripheral.discoverAttributes()) { | ||
- | | + | |
// retrieve the remote characteristic to read | // retrieve the remote characteristic to read | ||
- | remoteCharacteristic = peripheral.characteristic(REMOTE_CHARACTERISTIC_UUID); | + | remoteCharacteristic = peripheral.characteristic(REMOTE_NOTIFY_CHARACTERISTIC_UUID); |
if (remoteCharacteristic) { | if (remoteCharacteristic) { | ||
- | + | // check if the characteristic can be read | |
- | // if the peripheral is connected display the value | + | if (remoteCharacteristic.canSubscribe()){ |
- | | + | remoteCharacteristic.subscribe(); |
- | + | ||
- | // check if the characteristic can be read | + | // if the peripheral is connected display the value |
- | if (remoteCharacteristic.canRead()){ | + | |
- | char char_text[16]; | + | |
- | | + | // check if the value was updated |
- | int i=remoteCharacteristic.valueLength(); | + | if (remoteCharacteristic.valueUpdated()) { |
- | lcd.setCursor(0, | + | |
- | lcd.write(char_text,i); | + | // yes, get the value, characteristic is 1 byte so use byte value |
- | } // if (remoteCharacteristic.canRead()) | + | byte value = 0; |
- | } // if (peripheral.connected()) | + | |
+ | lcd.setCursor(0, | ||
+ | lcd.print(value); | ||
+ | |||
+ | | ||
+ | } // while (peripheral.connected()) | ||
+ | } // if (remoteCharacteristic.canSubscribe()) | ||
} // if (remoteCharacteristic) | } // if (remoteCharacteristic) | ||
peripheral.disconnect(); | peripheral.disconnect(); | ||
Line 216: | Line 136: | ||
==== Result validation ==== | ==== Result validation ==== | ||
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' | 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 ===== | ||
- | + | **What | |
- | **What | + | |
- | \\ | + | |
- | **What | + | |
- | \\ | + | |
- | **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 | + | |
\\ | \\ | ||
+ | **Is the notification/ | ||
<WRAP noprint> | <WRAP noprint> |