Fyzikální kabinet FyzKAB

Články Moduly ESP32 a ESP32-CAM Začínáme s ESP32 Bluetooth Low Energy

Začínáme s ESP32 Bluetooth Low Energy (BLE)

V minulém článku věnovanému Bluetooth jsme si ukázali základní použití Bluetooth Classic v modulu ESP32. Dnes bychom se rádi podívali na tzv. Bluetooth Low Energy (BLE). Nejprve v rychlosti si trochu zopakujeme, co je BLE a k čemu se dá použít. Poté se podíváme na několik příkladů s modulem ESP32, který budeme programovat pomocí Arduino IDE. Nakonec bychom si měli z modulu ESP32 vytvořit jednoduchý BLE server, ale také i BLE skener, který předchozí server najde.

Co je Bluetooth Low Energy?

Bluetooth Low Energy alias BLE (dříve uváděný na trh jako Bluetooth Smart) je vysoce optimalizovaná verze klasického Bluetooth navržená speciálně pro bezdrátovou komunikaci s nízkou spotřebou. Primárním účelem BLE je přenos malého množství dat na krátkou vzdálenost (nízká šířka pásma). Na rozdíl od Bluetooth, které je vždy zapnuté, zůstává BLE v režimu spánku, ze kterého se probudí, jen když je zahájeno připojení. Díky tomu má velmi nízkou spotřebu energie. BLE spotřebuje přibližně 100× méně energie než Bluetooth (v závislosti na případu použití). BLE navíc podporuje nejen komunikaci typu point-to-point, ale také režim vysílání a síť typu Mesh.

Zjednodušeně řečeno, každé zařízení Mesh sítě je kromě své funkce zároveň routerem, který může přenášet informace od ostatních zařízení. To zvyšuje spolehlivost sítě a její dosah. Zatímco standardní BT vysílání dosáhne řádově deseti metrů, Bluetooth Low Energy desítek metrů, s pomocí síťového propojení Mesh se reálný dosah pohybuje ve stovkách metrů, pokud jsou pokryty zařízeními, schopnými předávat informace.

Díky svým vlastnostem je BLE vhodný pro aplikace, které potřebují vyměňovat malé objemy dat, například využití ve zdravotnictví, ve fitness náramcích, v zabezpečovací technice a obecně v domácí automatizaci.

BLE

S Bluetooth Low Energy existují dva typy zařízení: server a klient. Modul ESP32 pochopitelně může fungovat buď jako klient nebo jako server. Server inzeruje svou existenci, takže jej může najít jiná zařízení a obsahuje data, která může klient číst. Klient prohledá okolní zařízení, a když najde hledaný server, naváže spojení a naslouchá příchozím datům. Tomu se říká komunikace point-to-point (česky též tzv. dvoubodový spoj).

BLE server and client
Zdroj obrázku: https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/


Obrázek 1 – princip komunikace komunikace Point-to-Point.


Jak již bylo zmíněno dříve, BLE také podporuje režim vysílání a Mesh síť:

  • Režim vysílání: server přenáší data mnoha klientům, kteří jsou připojeni
  • Mesh síť: všechna zařízení jsou připojena, jedná se o mnohonásobné připojení.

Sítě Mesh byly vyvinuty velmi nedávno, takže v tuto chvíli není mnoho příkladů implementovaných pro modul ESP32, ale budeme se snažit něco pro Vás najít a vyzkoušet. Pokud již nějaký tokový příklad máte, budeme vděčni, když se s námi o své zkušenosti podělíte.

GATT

GATT je zkratka pro Generic Attributes a definuje hierarchickou datovou strukturu, která je vystavena připojeným zařízením BLE. To znamená, že GATT definuje způsob, jakým dvě zařízení BLE odesílají a přijímají standardní zprávy. Pochopení této hierarchie je důležité, protože vám usnadní pochopení toho, jak používat BLE a psát své aplikace.

GATT BLE
Zdroj obrázku: https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/


Obrázek 2 – Hierarchická datová struktura GATT.

Služba BLE

Nejvyšší úrovní hierarchie je profil, který se skládá z jedné nebo více služeb. Zařízení BLE obvykle obsahuje více než jednu službu. Každá služba obsahuje alespoň jednu charakteristiku, případně může odkazovat i na jiné služby. Služba je jednoduše sbírka informací, jako jsou například údaje ze senzorů. Existují předdefinované služby pro několik typů dat definovaných SIG (Bluetooth Special Interest Group), jako jsou: stav baterie, krevní tlak, srdeční frekvence, hmotnost atd.

gatt services
Zdroj obrázku: https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/


Obrázek 3 – Několik typů dat definovaných SIG.

Charakteristika BLE

Charakteristiku vždy určuje daná služba a je to místo, kde jsou v hierarchii (hodnotě) obsažena skutečná data. Charakteristika má vždy dva atributy: deklaraci charakteristiky (která poskytuje metadata o datech) a hodnotu charakteristiky. Navíc za charakteristickou hodnotou mohou následovat deskriptory, které dále rozšiřují metadata obsažená v deklaraci charakteristiky. Vlastnosti popisují, jak může být charakteristická hodnota ovlivněna. V zásadě obsahuje operace a postupy, které lze použít s charakteristikou:

  • Broadcast (Přenos)
  • Read (Čtení)
  • Write without response (Zápis bez odezvy)
  • Write (Zápis)
  • Notify (Oznámení)
  • Indicate (Označení)
  • Authenticated Signed Writes (Ověřený zápisy)
  • Extended Properties (Rozšířené vlastnosti)

UUID

Každá služba, charakteristika a deskriptor mají UUID (Universally Unique Identifier). UUID je jedinečné 128bitové (16 bajtové) číslo.

Například:
55072829-bc9e-4c53-938a-74a6d4c78776

Existují zkrácené UUID pro všechny typy, služby a profily specifikované v SIG (Bluetooth Special Interest Group). Pokud však naše aplikace potřebuje vlastní UUID, můžeme jej vygenerovat pomocí této webové stránky generátoru UUID. Stručně řečeno, UUID se používá pro jednoznačnou identifikaci informací. Může například identifikovat konkrétní službu poskytovanou zařízením Bluetooth.

BLE a modul ESP32

ESP32 může fungovat jako BLE server nebo jako BLE klient. Původně externí knihovna ESP32_BLE_Arduino, kterou budeme dále používat, je již součástí prostředí Arduino IDE, takže tuto knihovnu ani nemusíme do prostředí Arduino IDE dodatečně instalovat. K dispozici máme i několik ukázkových programů – pokud se na ně chcete podívat, přejděte ve svém prostředí Arduino IDE na Soubor > Příklady > ESP32 BLE Arduino.

gatt services


Obrázek 4 – Nalezení ukázkových kódů k  problematice BLE (v prostředí Arduino IDE).



   Poznámka:
Chcete-li vidět příklady pro modul ESP32, musíte mít desku s ESP32 vybranou v nabídce Nástroje >  Vývojová deska.
 

Modul ESP32 jako BLE Server

Při vytvoření BLE serveru by měly v kódu našeho programu následovat následující kroky:

  1. Vytvořit objekt BLE serveru.
  2. Vytvořit službu BLE.
  3. Vytvořit charakteristiku BLE pro službu.
  4. Vytvořit deskriptor BLE pro charakteristiku.
  5. Spustit službu.
  6. Začít se „inzerovat“, aby jej mohla najít jiná zařízení.

Nevypadá to jednoduše, co? Tak raději pro začátek použijeme něco již hotového a postupně si kód vysvětlíme. Pro náš účel použijeme základní ukázkový program BLE_server. Otevřeme prostředí Arduino IDE a přejděte na  Soubor > Příklady > ESP32 BLE Arduino a vyberte příklad BLE_serveru.

Měl by se načíst následující kód:

/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
*/

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");   // Long name works now
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                                       CHARACTERISTIC_UUID,
                                                       BLECharacteristic::PROPERTY_READ |
                                                       BLECharacteristic::PROPERTY_WRITE
                                                       );
  pCharacteristic->setValue("Hello World says Neil");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();   // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);   // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}


Jak kód funguje

Pojďme se tedy podívat na to, jak tento ukázkový kód serveru BLE funguje.

Začíná importem knihoven nezbytných pro funkce BLE.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

Poté musíte definovat UUID pro službu a charakteristiku.

#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

UUID můžeme ponechat výchozí, nebo můžeme přejít na uuidgenerator.net a vytvořit náhodná UUID pro naše služby a vlastnosti.

V proceduře setup() se kromě nastavení sériového portu vytvoří objekt zařízení BLE s názvem „MyESP32“. (Tento název si můžeme změnit na jakýkoli jiný).

// Create the BLE Device
BLEDevice::init("MyESP32");

Na následujícím řádku se zařízení BLE nastaví jako server.

BLEServer *pServer = BLEDevice::createServer();

Poté se vytvoří služba pro server BLE s dříve definovaným UUID.

BLEService *pService = pServer->createService(SERVICE_UUID);

V dalším kroku musíme nastavit charakteristiku pro tuto službu. Používáme UUID definovanou dříve a jako argumenty musíte předat vlastnosti charakteristiky. V tomto případě to jsou: ČTENÍ a ZÁPIS.

BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                                       CHARACTERISTIC_UUID,
                                                       BLECharacteristic::PROPERTY_READ |
                                                       BLECharacteristic::PROPERTY_WRITE
                                                       );

Po vytvoření charakteristiky můžete nastavit její hodnotu pomocí metody setValue().

pCharacteristic->setValue("Hello World says Neil");

V tomto případě nastavujeme hodnotu na text „Hello World says Neil“. Tento text opět můžeme změnit na svůj libovolný. V budoucích projektech může být tímto textem například čtení senzoru nebo stav LED.

Konečně můžete spustit službu a „inzerci“, takže ostatní zařízení BLE mohou toto zařízení BLE skenovat a najít.

BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();

Toto je jen jednoduchý příklad, jak vytvořit BLE server. V tomto kódu se nic nedělá, proto je hlavní smyčka loop() prázdná, ale můžete přidat, co se má stát, když se připojí nový klient (pokyny najdete v příkladu BLE_notify).

Modul ESP32 jako BLE skener

Pokud jsme pochopili princip předchozího příkladu, mělo by být vytvoření BLE skeneru poměrně jednoduché. Abychom měli co skenovat, necháme předchozí program běžet na jednom modulu ESP32 a vezmeme další vývojovou deku s ESP32 a naprogramujeme ji jako BLE skener. V prostředí Arduino IDE přejděte na Soubor >  Příklady >  ESP32 BLE Arduino a vyberte příklad BLE_scan.

Měl by se načíst následující kód:

/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

int scanTime = 5;   //In seconds

BLEScan* pBLEScan;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
  };

void setup() {
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();   //create new scan
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);   //active scan uses more power, but get results faster
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);   //less or equal setInterval value
}

void loop() {
  // put your main code here, to run repeatedly:
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory
  delay(2000);
}


Tento kód inicializuje modul ESP32 jako zařízení BLE a vyhledá okolní zařízení. Vyzkoušíme ho tedy tak, že jej nahrajeme do druhého modulu ESP32. (Možná nebude od věci, dočasně odpojit druhý ESP32 od počítače, abychom si byli jisti, že nahráváme kód na správnou vývojovou desku s modulem ESP32. 😉).

Jakmile je kód nahrán a měli byste mít zapnuté dvě desky ESP32:

  • Jednu vývojovou desku s modulem ESP32, která je naprogramována jako BLE_server,
  • Druhou vývojovou desku s programem BLE_scan.

Přejdeme na sériový monitor modulu ESP32 se spuštěným programem BLE_scan, tento modul restartujeme (tlačítko EN, resp. RST). Po restartu počkáme několik sekund, než proběhne skenování BLE okolních zařízení.

esp32 scan + esp32 server
Zdroj obrázku: https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/


Obrázek 5 – Vyhledávání BLE serveru na moduly ESP32 pomocí BLE skeneru na druhém ESP32


Po přeložení kódu, jeho nahrání do modulu ESP32 bychom měli v okně sériového monitoru vidět výstup podobný následujícímu obrázku.

BLE-skener serial


Obrázek 6 – Výstup BLE skeneru - seznam nalezených zařízení


Na obrázku č. 6 dokonce vidíme, že náš BLE skener nalezl nejen modul ESP32, na kterém běží BLE server, ale i fitness náramek, který byl v blízkosti. 😎

Testování BLE serveru na modulu ESP32 pomocí smartphonu

Většina moderních smartphonů dnes již BLE funkce podporuje, takže náš BLE server na modulu ESP32 by se měl dát chytrým telefonem naskenovat a prohlédnout si jeho služby a vlastnosti. K tomu využijeme bezplatnou aplikaci s názvem nRF Connect for Mobile od společnosti Nordic® Semiconductor. Tato aplikace je dostupná pro telefony jak s operačním systémem Android (odkaz na Google Play Store), tak systémem iOS (odkaz na App Store).

nRF Play-Store


Obrázek 7 – Aplikace nRF Connect for Mobile na smartphonu s operačním systémem Android.


Přejdeme tedy do obchodu Google Play nebo App Store a vyhledáma a nainstalujeme aplikaci nRF Connect for Mobile.

Po spuštění aplikace nezapomeňte přejít do nastavení Bluetooth a povolit adaptér Bluetooth. Můžete jej také zviditelnit pro ostatní zařízení, abyste mohli později testovat další programy.

nastaveni-nRF krok 1


Obrázek 8 – První nastavení aplikace nRF Connect for Mobile.


Jakmile je vše připraveno ve smartphonu, spustíme v modulu ESP32 program BLE_server. V pravém horním rohu okna aplikace klikneme na tlačítko SCAN a mobil začne vyhledávat zařízení v okolí. Měli bychom mimo jiného najít i modul ESP32 s programem BLE server, pojmenovaný MyESP32.

nastaveni-nRF krok 1


Obrázek 9 – Spárování smartphonu se BLE serverem na modulu ESP32.


U našeho ESP32 BLE serveru (MyESP32) klikneme na tlačítko CONNECT.

Jak můžete vidět na obrázku níže, ESP32 má službu s UUID, které jste definovali dříve. Pokud klepnete na službu (Unknown Service), popřípadě klepneme na nabídku Read characteristics (v menu tří teček v šedé listě), zobrazí se charakteristika s UUID, které jste také definovali.

nRF nacteno


Obrázek 10 – Výpis služby BLE poskytované BLE serverem.


Charakteristika má vlastnosti READ a WRITE a hodnota je hodnota, kterou jste dříve definovali v náčrtu serveru BLE. Takže vše funguje dobře.

Konečně „užitečný“ program?

Na předchozích programech jsme si ukázali obecné chování BLE serveru. Teď zkusíme vytvořit program, který již mohl vykonávat nějakou „užitečnou“ práci.

Jako jednu ukázku zde odkazujeme na následující článek s program, který jsme našli na severu DRÁTEK.cz: ESP32 a Bluetooth Low Energy (BLE) na serveru DRÁTEK. Jedná se o program, který (pokud se někdo připojí) začne načítat analogový pin GPIO 32, ke kterému být připojeno nějaké analogové čidlo (např. teploměr LM35, potenciometr reagující na polohu ). Tyto hodnoty odesílá na připojené zařízení. Jakmile se zařízení odpojí, vysílání přestane. Zároveň má tento program ještě jednu funkci. Připojené zařízení může na tento měřicí server zasílat řídicí zprávy   v tomto případě to je buď znak A, který rozsvítí LED připojenou na GPIO 2 (zabudovaná LED na vývojovém kitu), stejně tak zaslaný znak B LED zhasne.

My se však podíváme na příklad jiný. Opustíme smartphone a vrátíme se k dvojici modulů ESP32. Jeden naprogramujeme jako server, který bude poskytovat nějakou hodnotu, druhý modul bude klient, který se na tuto hodnotu bude dotazovat. A aby to nebylo zas tak úplně triviální, uděláme, aby klient mohl i hodnotu na serveru přepsat.

Program Server BLE

V tomto případě se vlastně jedná o mírně upravený ukázkový program BLE_server z prostředí Arduino IDE.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLEServer *pServer;
BLEService *pService;
BLECharacteristic *pCharacteristic;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE Server!");

  BLEDevice::init("ESP32-BLE-Server");
  pServer = BLEDevice::createServer();
  pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(
                                 CHARACTERISTIC_UUID,
                                 BLECharacteristic::PROPERTY_READ |
                                 BLECharacteristic::PROPERTY_WRITE
                                 );

  pCharacteristic->setValue("Hello, World!");
  pService->start();

  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);   // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();

  Serial.println("Charakteristika definovana! Nyni si ji muzete precist klientem!");
}

void loop() {
  std::string value = pCharacteristic->getValue();
  Serial.print("Nova hodnota charakteristicky je: ");
  Serial.println(value.c_str());

  delay(2000);
}


Program Klient BLE

Na další desku ESP32, kterou budeme nazývat ESP32-BLE_Client nahrajeme následující program. Opět se jedná o ukázkový program z prostředí Arduino IDE, který je drobně upraven.

#include "BLEDevice.h"

/* Specify the Service UUID of Server */
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
/* Specify the Characteristic UUID of Server */
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;

static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify) {
  Serial.print("Notify callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  Serial.print(" of data length ");
  Serial.println(length);
  Serial.print("data: ");
  Serial.println((char*)pData);
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {

  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    Serial.println("onDisconnect");
  }
};

/* Start connection to the BLE Server */
bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(myDevice->getAddress().toString().c_str());
  BLEClient* pClient = BLEDevice::createClient();
  Serial.println(" - Created client");

  pClient->setClientCallbacks(new MyClientCallback());

  /* Connect to the remote BLE Server */
  pClient->connect(myDevice);   // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
  Serial.println(" - Connected to server");

  /* Obtain a reference to the service we are after in the remote BLE server */
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");

  /* Obtain a reference to the characteristic in the service of the remote BLE server */
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  /* Read the value of the characteristic */
  /* Initial value is 'Hello, World!' */
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  if(pRemoteCharacteristic->canNotify()) {
    pRemoteCharacteristic->registerForNotify(notifyCallback);
  }
  connected = true;
  return true;
}

/* Scan for BLE servers and find the first one that advertises the service we are looking for. */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  /* Called for each advertising BLE server. */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    /* We have found a device, let us now see if it contains the service we are looking for. */
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;
    }
  }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("ESP32-BLE-Client");

  /* Retrieve a Scanner and set the callback we want to use to be informed when we
    have detected a new device. Specify that we want active scanning and start the
    scan to run for 5 seconds. */
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
}

void loop() {
  /* If the flag "doConnect" is true, then we have scanned for and found the desired
    BLE Server with which we wish to connect. Now we connect to it. Once we are
    connected we set the connected flag to be true. */
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  /* If we are connected to a peer BLE Server, update the characteristic each time we are reached
    with the current time since boot */
  if (connected) {
    String newValue = "Time since boot: " + String(millis()/2000);
    Serial.println("Setting new characteristic value to \"" + newValue + "\"");

    /* Set the characteristic's value to be the array of bytes that is actually a string */
    pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
    /* You can see this value updated in the Server's Characteristic */
  }
  else 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(2000);   /* Delay a second between loops */
}


Po nahrání obou kódů na obě desky ESP32 by bylo dobré sledovat sériové porty obou programů, abychom viděli vzájemnou komunikaci. K tomu je možné použít standardní sériový monitor prostředí Arduino IDE pro otevření COM jedné z připojených desek a nějaký externí program (seriál monitor) pro otevření COM portu druhé desky. Jinou možností je zapojit jednu desku k jednomu počítači a druhou desku k druhému počítači (např. notebooku) a na obou otevřít sériové portu prostředí Arduino IDE. Na sériových portech se budou vypisovat jednotlivé okamžiky vzájemné komunikace.

Zařízení ESP32 BLE Server spustí BLE Server a začne inzerovat své služby. Pokud jsme v kód ESP32 klienta použili stejný kód UUID služby a UUID charakteristiky, klient ESP32 vyhledá toho zařízení. Pokud je nalezeno BLE zařízení s konkrétními UUID, vytvoří spojení. Jakmile je spojení navázáno, klient ESP32 BLE nejprve načte charakteristickou hodnotu ze serveru (v kódu serveru nastavíme „Hello, World!“) a vytiskne ji na terminál. Poté se klient začne s intervalem několika sekund postupně nastavovat různou hodnotu charakteristicky serveru novou hodnotou.

Terminál klienta můžete vidět na následujícím obrázku č. 11.

okno serial monitoru - Klient
Zdroj obrázku: https://www.electronicshub.org/esp32-ble-tutorial/


Obrázek 11 – Výstup na sériovém portu klienta.
Nalezení serveru, první načtení charakteristiky, pravidelný zápis do serveru.


Při připojení klienta server vypisuje na svůj seriový port hodnotu charakteristiky. Na výpisu bychom viděli téměř totéž, co na předešlém obrázku č. 11. Charakteristika nejdříve obsahuje „Hello, World!“ a pak je v pravidelných intervalech přepisována klientem (viz obrázek č. 12)

okno serial monitoru - Server
Zdroj obrázku: https://www.electronicshub.org/esp32-ble-tutorial/


Obrázek 12 – Výstup na sériovém portu na straně serveru.
Odeslání charakteristiky klientovi a zápis charakteristiky od klienta.

Závěr

Popravdě řečeno, ještě bychom se měli podrobněji podívat, jak vytvořit na modulu ESP32 program BLE klient. V předešlé ukázce jsme sice klienta používali, ale moc jsme si jej nevysvětlili. Ale nebojte, vrátíme se k tomu opět při řešení konkrétního zapojení – chystáme bezdrátové načítání vlhkosti půdy u pokojových květin To je asi přesná ukázka použití přenosu dat způsobem BLE. Jeden modul ESP32 bude načítat vlhkost půdy 1× za hodinu a zároveň bude odesílat data na druhý modul ESP32, který takto bude shromažďovat data (a to nejlépe od několika čidel!). Kromě okamžiku odesílání dat musí oba moduly ESP32 mít minimální spotřebu energie. Pochopitelně v případě potřeby zálivky se spustí „zahradnický alarm“ (tam pak energií šetřit nebudeme! 😉). Ale o tom opravdu až při řešení tohoto projektu!

Pro dnešek se asi shodneme, že už toho Bluetooth bylo dost! A příště se zase sejdeme u nějakého „bastlířsky přívětivějšího“ tématu. 🤓


UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!