User Tools

Site Tools


No ACL setup yet! Denying access to everyone.
en:edu:zmitac:s2:iotn:esp32ble4

Differences

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

Link to this comparison view

Next revision
Previous revision
en:edu:zmitac:s2:iotn:esp32ble4 [2024/04/01 13:59] – created ktokarzen:edu:zmitac:s2:iotn:esp32ble4 [2024/04/01 20:08] (current) – [FAQ] ktokarz
Line 18: Line 18:
  
 === Step 1 === === Step 1 ===
-We start with the program written during the laboratory IoT_8 by including the BLE2902.h library which handles the Client Characteristic Configuration Descriptor (CCCD). The descriptor of this type is used to enable or disable the notification and indication feature. +In this step, we will analyse the behaviour of the client. The client software is much more complex than the server. It is because the central device in many circumstances is a more powerful device than the peripheral. Some parts of the software are implemented as callback functions because they handle reactions on the data coming asynchronously from the server. The diagram presents the algorithm of the client and data coming from the server. 
-^ Descriptor UUID  ^ Bits 1 and 0  ^ CCCD value  ^ Function                  ^ +{{ :en:edu:zmitac:s2:iotn:ble_client.drawio.svg |The client algorithm}} 
-| 0x2902           | 00            | 0           | notify/indicate disabled  | + 
-| :::              | 01            | 1           | enable notification       | +=== Step 2 === 
-| :::              | 10            | 2           | enable indication         |+We start implementation with the libraries needed. 
 <code> <code>
-#include "BLE2902.h"+#include "Arduino.h" 
 +#include "BLEDevice.h"
 </code> </code>
-Next, we add another characteristic to the service and variable required to hold the pointer to the characteristic class. We need to define the UUID for this characteristic.+Next, we define the UUIDs for remote service and characteristic. Notice they must match the ones defined in the server.
 <code> <code>
-BLECharacteristic  *pNotifyCharacteristic; //class for the characteristic +// The remote service we wish to connect to. 
-#define NOTIFY_CHARACTERISTIC_UUID "6e9b7b28-ca96-4774-b056-8ec5b759fd86"+#define SERVICE_UUID "6e9b7b26-ca96-4774-b056-8ec5b759fd86" 
 +// The characteristic of the remote service we are interested in. 
 +#define REMOTE_CHARACTERISTIC_UUID "6e9b7b28-ca96-4774-b056-8ec5b759fd86"
 </code> </code>
-We create an additional characteristic with reading, writing, notify and indicate enabled, and the initial text "00" as a placeholder for the data. We also add the descriptor to the characteristic.+ 
 +Some global variables will be needed for our software.
 <code> <code>
-// Create a BLE Characteristic +// Variables 
-pNotifyCharacteristic = pService->createCharacteristic( +static boolean doConnect = false; 
-                      NOTIFY_CHARACTERISTIC_UUID, +static boolean connected = false; 
-                      BLECharacteristic::PROPERTY_READ   | +static boolean doScan = false; 
-                      BLECharacteristic::PROPERTY_WRITE  | +static BLERemoteCharacteristic* pRemoteCharacteristic; 
-                      BLECharacteristic::PROPERTY_NOTIFY | +static BLEAdvertisedDevice* myDevice;
-                      BLECharacteristic::PROPERTY_INDICATE +
-                    ); +
-pNotifyCharacteristic->setValue("0"); +
-// Create a BLE Descriptor +
-pNotifyCharacteristic->addDescriptor(new BLE2902());+
 </code> </code>
  
-<note> +Three callback functions will be defined. The first callback is for advertising. 
-Any characteristic can be configured as notify or indicate enabled. We can enable this mechanism for more than one characteristic. +<code> 
-</note>+// Scan for BLE servers and find the first one that advertises  
 +// the service we are looking for. 
 +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 
 +  void onResult(BLEAdvertisedDevice advertisedDevice) { 
 +    // 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))) {
  
-After these modifications, it would be possible to observe an additional characteristic in the service with the possibility of reading, writing, and enabling notification and indication.+      BLEDevice::getScan()->stop(); 
 +      myDevice = new BLEAdvertisedDevice(advertisedDevice); 
 +      doConnect = true; 
 +      doScan = true;
  
-=== Step 2 === +    } // Found our server 
-At this step, we implement periodical data sending. We add two variables, an integer for holding the value incremented every loop pass and a text value converted from an integer to be sent within the indication/notification packet. +  } // onResult 
-<code c> +}; // MyAdvertisedDeviceCallbacks
-int int_value=0; +
-char text_value[] = "000000";+
 </code> </code>
-In the loop() function we modify the part executed if the connection was established. We'll add conversion from integer to text with atoi(), an update of characteristic value, and an incrementation of integer value.+ 
 +Another callback will be executed on the notify event. In the example, we simply print incoming data to the console, but you can use the data in any other way.
 <code> <code>
-void loop(){ +static void notifyCallback( 
-    if (!deviceConnected && !advStarted) { +  BLERemoteCharacteristic* pBLERemoteCharacteristic, 
-        pServer->startAdvertising(); // restart advertising +  uint8_t* pData, 
-        advStarted = true; +  size_t length, 
-    }+  bool isNotify) { 
 +    Serial.print("Notify data: "); 
 +    Serial.write(pData, length); 
 +    Serial.println(); 
 +} 
 +</code>
  
-    if (deviceConnected){ +The third callback class helps to inform the other parts of the program about the connection close. 
-        advStarted = false; +<code> 
-        itoa(int_value,text_value,10); +class MyClientCallback : public BLEClientCallbacks { 
-        pNotifyCharacteristic->setValue(text_value); +  void onConnect(BLEClient* pclient) { 
-        pNotifyCharacteristic->notify(); +  }
-        int_value++; +
-    }+
  
-    delay(500);+  void onDisconnect(BLEClient* pclient) { 
 +    connected = false; 
 +  }
 }; };
 </code> </code>
 +
 +The setup function initialises the Bluetooth, registers the advertising callback function, and starts the scan to look for nearby devices.
 +<code>
 +void setup() {
 +  Serial.begin(115200);
 +  Serial.println("BLE Client app");
 +  
 +  // Initialise the Bluetooth
 +  BLEDevice::init("");
 +
 +  // Retrieve the pointer to the scan module
 +  BLEScan* pBLEScan = BLEDevice::getScan();
 +  
 +  // Register callback for incoming advertising
 +  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
 +  
 +  // Start scan parameters with active scan mode
 +  pBLEScan->setInterval(1349);
 +  pBLEScan->setWindow(449);
 +  pBLEScan->setActiveScan(true);
 +  
 +  // Start the scan for 30 seconds
 +  pBLEScan->start(30, false);
 +}
 +</code>
 +<note>
 +In the original example, the scan time is 5 seconds only. It can be too short to connect to a remote device, especially if there are other devices in the neighbourhood.
 +</note>
 +In the loop function, we wait for the information that the server with the service UUID we were interested in was found. It is signalled with the use of "doConnect" flag. If 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.
 +<code>
 +void loop() {
 +  if (doConnect == true) {
 +    connectToServer();
 +    doConnect = false;
 +  }
 +
 +  if (!connected) {
 +    if(doScan){
 +    BLEDevice::getScan()->start(0);  // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
 +  }
 +  delay(1000); // Delay a second between loops.
 +} 
 +</code>
 +
 +The connection to the server is executed in some steps:
 +  - Creation of the client object.
 +  - Setting the callback class for handling disconnection.
 +  - Connection to the remote device.
 +  - Setting parameters of the connection.
 +  - Getting the reference to the service.
 +  - Getting the reference to the characteristic.
 +  - Registering notification for the characteristic and setting the notify callback function.
 +  - Informing the main program with the "connected" flag.
 +
 +<code>
 +bool connectToServer() {
 +    BLEClient*  pClient  = BLEDevice::createClient();
 +    pClient->setClientCallbacks(new MyClientCallback());
 +
 +    // Connect to the remote BLE Server.
 +    pClient->connect(myDevice);  
 +    
 +    pClient->setMTU(517); 
 +  
 +    // Obtain a reference to the service we are after in the remote BLE server.
 +    BLERemoteService* pRemoteService = pClient->getService(BLEUUID(SERVICE_UUID));
 +    
 +    // Obtain a reference to the characteristic in the service of the remote BLE server.
 +    pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(REMOTE_CHARACTERISTIC_UUID));
 +    
 +    if(pRemoteCharacteristic->canNotify())
 +      pRemoteCharacteristic->registerForNotify(notifyCallback);
 +
 +    connected = true;
 +    return true;
 +}
 +</code>
 +
 +<note>
 +Because this example is quite complex, it's good to have messages sent by the program. The original example contains many messages printed at almost every step of the program's operation.
 +</note>
 +
 ==== Result validation ==== ==== Result validation ====
-You should be able to find the device in the BLE neighbourhood. It should allow us to establish a connection, read the characteristic data and write a new value. After enabling the notification we will observe periodic incrementation of the value sent with data packets. +You should be able to find the server device in the BLE neighbourhood, connect to it, subscribe to the notification and receive the incoming data. We should be able to observe periodic increments of the value received with data packets from the server.
  
 ===== FAQ ===== ===== FAQ =====
-**What is the difference between notification and indication?**: As it was mentioned in the beginning the notification is an unacknowledged message while an indication is an acknowledged message. It means that if the indication message remains unacknowledged, the peripheral device won't send another message for this characteristic.+**Can we connect to the same peripheral with more than one central device?**: No, it's not possible. The BLE standard allows only one connection for the peripheral device. It was defined to ensure that BLE will work on constrained devices with small memory.
  
-**Is the notification/indication mechanism similar to interrupts?**: You are right. They work in a similar manner as interrupts in the microprocessor. They allow to sending of the data asynchronously, and the peripheral decides about the time of the message sent like the peripheral decides on the time of signalling interrupt. Data transmission when the central device sends the packet with a characteristic read command is similar to the polling method used in microprocessors.+**May I use the data in another way than printing it on the console?**: Of course. You can for example change the LED colour on the central device depending on the temperature readings from the peripheral device.
  
 <WRAP noprint> <WRAP noprint>
en/edu/zmitac/s2/iotn/esp32ble4.1711972795.txt.gz · Last modified: by ktokarz