├── README.md ├── examples ├── BLE_client │ └── BLE_client.ino ├── BLE_iBeacon │ └── BLE_iBeacon.ino ├── BLE_notify │ └── BLE_notify.ino ├── BLE_scan │ └── BLE_scan.ino ├── BLE_server │ └── BLE_server.ino ├── BLE_server_multiconnect │ └── BLE_server_multiconnect.ino ├── BLE_uart │ └── BLE_uart.ino └── BLE_write │ └── BLE_write.ino ├── library.properties └── src ├── BLE2902.cpp ├── BLE2902.h ├── BLE2904.cpp ├── BLE2904.h ├── BLEAddress.cpp ├── BLEAddress.h ├── BLEAdvertisedDevice.cpp ├── BLEAdvertisedDevice.h ├── BLEAdvertising.cpp ├── BLEAdvertising.h ├── BLEBeacon.cpp ├── BLEBeacon.h ├── BLECharacteristic.cpp ├── BLECharacteristic.h ├── BLECharacteristicMap.cpp ├── BLEClient.cpp ├── BLEClient.h ├── BLEDescriptor.cpp ├── BLEDescriptor.h ├── BLEDescriptorMap.cpp ├── BLEDevice.cpp ├── BLEDevice.h ├── BLEEddystoneTLM.cpp ├── BLEEddystoneTLM.h ├── BLEEddystoneURL.cpp ├── BLEEddystoneURL.h ├── BLEExceptions.cpp ├── BLEExceptions.h ├── BLEHIDDevice.cpp ├── BLEHIDDevice.h ├── BLERemoteCharacteristic.cpp ├── BLERemoteCharacteristic.h ├── BLERemoteDescriptor.cpp ├── BLERemoteDescriptor.h ├── BLERemoteService.cpp ├── BLERemoteService.h ├── BLEScan.cpp ├── BLEScan.h ├── BLESecurity.cpp ├── BLESecurity.h ├── BLEServer.cpp ├── BLEServer.h ├── BLEService.cpp ├── BLEService.h ├── BLEServiceMap.cpp ├── BLEUUID.cpp ├── BLEUUID.h ├── BLEUtils.cpp ├── BLEUtils.h ├── BLEValue.cpp ├── BLEValue.h ├── FreeRTOS.cpp ├── FreeRTOS.h ├── GeneralUtils.cpp ├── GeneralUtils.h ├── HIDKeyboardTypes.h └── HIDTypes.h /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 BLE for Arduino 2 | 3 | This repository is deprecated. The BLE code is now part of the ESP32 Arduino core, making it automatically available to any Arduino ESP32 project without the need for manual import. For more information, see [ESP32 Arduino BLE Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE). 4 | ---------------- 5 | 6 | The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino. 7 | 8 | The actual source of the project which is being maintained can be found here: 9 | 10 | https://github.com/nkolban/esp32-snippets 11 | 12 | Issues and questions should be raised here: 13 | 14 | https://github.com/nkolban/esp32-snippets/issues 15 | 16 | 17 | Documentation for using the library can be found here: 18 | 19 | https://github.com/nkolban/esp32-snippets/tree/master/Documentation 20 | -------------------------------------------------------------------------------- /examples/BLE_client/BLE_client.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * A BLE client example that is rich in capabilities. 3 | * There is a lot new capabilities implemented. 4 | * author unknown 5 | * updated by chegewara 6 | */ 7 | 8 | #include "BLEDevice.h" 9 | //#include "BLEScan.h" 10 | 11 | // The remote service we wish to connect to. 12 | static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); 13 | // The characteristic of the remote service we are interested in. 14 | static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); 15 | 16 | static boolean doConnect = false; 17 | static boolean connected = false; 18 | static boolean doScan = false; 19 | static BLERemoteCharacteristic* pRemoteCharacteristic; 20 | static BLEAdvertisedDevice* myDevice; 21 | 22 | static void notifyCallback( 23 | BLERemoteCharacteristic* pBLERemoteCharacteristic, 24 | uint8_t* pData, 25 | size_t length, 26 | bool isNotify) { 27 | Serial.print("Notify callback for characteristic "); 28 | Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); 29 | Serial.print(" of data length "); 30 | Serial.println(length); 31 | Serial.print("data: "); 32 | Serial.println((char*)pData); 33 | } 34 | 35 | class MyClientCallback : public BLEClientCallbacks { 36 | void onConnect(BLEClient* pclient) { 37 | } 38 | 39 | void onDisconnect(BLEClient* pclient) { 40 | connected = false; 41 | Serial.println("onDisconnect"); 42 | } 43 | }; 44 | 45 | bool connectToServer() { 46 | Serial.print("Forming a connection to "); 47 | Serial.println(myDevice->getAddress().toString().c_str()); 48 | 49 | BLEClient* pClient = BLEDevice::createClient(); 50 | Serial.println(" - Created client"); 51 | 52 | pClient->setClientCallbacks(new MyClientCallback()); 53 | 54 | // Connect to the remove BLE Server. 55 | pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) 56 | Serial.println(" - Connected to server"); 57 | 58 | // Obtain a reference to the service we are after in the remote BLE server. 59 | BLERemoteService* pRemoteService = pClient->getService(serviceUUID); 60 | if (pRemoteService == nullptr) { 61 | Serial.print("Failed to find our service UUID: "); 62 | Serial.println(serviceUUID.toString().c_str()); 63 | pClient->disconnect(); 64 | return false; 65 | } 66 | Serial.println(" - Found our service"); 67 | 68 | 69 | // Obtain a reference to the characteristic in the service of the remote BLE server. 70 | pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); 71 | if (pRemoteCharacteristic == nullptr) { 72 | Serial.print("Failed to find our characteristic UUID: "); 73 | Serial.println(charUUID.toString().c_str()); 74 | pClient->disconnect(); 75 | return false; 76 | } 77 | Serial.println(" - Found our characteristic"); 78 | 79 | // Read the value of the characteristic. 80 | if(pRemoteCharacteristic->canRead()) { 81 | std::string value = pRemoteCharacteristic->readValue(); 82 | Serial.print("The characteristic value was: "); 83 | Serial.println(value.c_str()); 84 | } 85 | 86 | if(pRemoteCharacteristic->canNotify()) 87 | pRemoteCharacteristic->registerForNotify(notifyCallback); 88 | 89 | connected = true; 90 | } 91 | /** 92 | * Scan for BLE servers and find the first one that advertises the service we are looking for. 93 | */ 94 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 95 | /** 96 | * Called for each advertising BLE server. 97 | */ 98 | void onResult(BLEAdvertisedDevice advertisedDevice) { 99 | Serial.print("BLE Advertised Device found: "); 100 | Serial.println(advertisedDevice.toString().c_str()); 101 | 102 | // We have found a device, let us now see if it contains the service we are looking for. 103 | if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { 104 | 105 | BLEDevice::getScan()->stop(); 106 | myDevice = new BLEAdvertisedDevice(advertisedDevice); 107 | doConnect = true; 108 | doScan = true; 109 | 110 | } // Found our server 111 | } // onResult 112 | }; // MyAdvertisedDeviceCallbacks 113 | 114 | 115 | void setup() { 116 | Serial.begin(115200); 117 | Serial.println("Starting Arduino BLE Client application..."); 118 | BLEDevice::init(""); 119 | 120 | // Retrieve a Scanner and set the callback we want to use to be informed when we 121 | // have detected a new device. Specify that we want active scanning and start the 122 | // scan to run for 5 seconds. 123 | BLEScan* pBLEScan = BLEDevice::getScan(); 124 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 125 | pBLEScan->setInterval(1349); 126 | pBLEScan->setWindow(449); 127 | pBLEScan->setActiveScan(true); 128 | pBLEScan->start(5, false); 129 | } // End of setup. 130 | 131 | 132 | // This is the Arduino main loop function. 133 | void loop() { 134 | 135 | // If the flag "doConnect" is true then we have scanned for and found the desired 136 | // BLE Server with which we wish to connect. Now we connect to it. Once we are 137 | // connected we set the connected flag to be true. 138 | if (doConnect == true) { 139 | if (connectToServer()) { 140 | Serial.println("We are now connected to the BLE Server."); 141 | } else { 142 | Serial.println("We have failed to connect to the server; there is nothin more we will do."); 143 | } 144 | doConnect = false; 145 | } 146 | 147 | // If we are connected to a peer BLE Server, update the characteristic each time we are reached 148 | // with the current time since boot. 149 | if (connected) { 150 | String newValue = "Time since boot: " + String(millis()/1000); 151 | Serial.println("Setting new characteristic value to \"" + newValue + "\""); 152 | 153 | // Set the characteristic's value to be the array of bytes that is actually a string. 154 | pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); 155 | }else if(doScan){ 156 | BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino 157 | } 158 | 159 | delay(1000); // Delay a second between loops. 160 | } // End of loop 161 | -------------------------------------------------------------------------------- /examples/BLE_iBeacon/BLE_iBeacon.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp 3 | Ported to Arduino ESP32 by pcbreflux 4 | */ 5 | 6 | 7 | /* 8 | Create a BLE server that will send periodic iBeacon frames. 9 | The design of creating the BLE server is: 10 | 1. Create a BLE Server 11 | 2. Create advertising data 12 | 3. Start advertising. 13 | 4. wait 14 | 5. Stop advertising. 15 | 6. deep sleep 16 | 17 | */ 18 | #include "sys/time.h" 19 | 20 | #include "BLEDevice.h" 21 | #include "BLEUtils.h" 22 | #include "BLEBeacon.h" 23 | #include "esp_sleep.h" 24 | 25 | #define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up 26 | RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory 27 | RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | uint8_t temprature_sens_read(); 34 | //uint8_t g_phyFuns; 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | // See the following for generating UUIDs: 41 | // https://www.uuidgenerator.net/ 42 | BLEAdvertising *pAdvertising; 43 | struct timeval now; 44 | 45 | #define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) 46 | 47 | void setBeacon() { 48 | 49 | BLEBeacon oBeacon = BLEBeacon(); 50 | oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) 51 | oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); 52 | oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); 53 | oBeacon.setMinor(bootcount&0xFFFF); 54 | BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); 55 | BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); 56 | 57 | oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 58 | 59 | std::string strServiceData = ""; 60 | 61 | strServiceData += (char)26; // Len 62 | strServiceData += (char)0xFF; // Type 63 | strServiceData += oBeacon.getData(); 64 | oAdvertisementData.addData(strServiceData); 65 | 66 | pAdvertising->setAdvertisementData(oAdvertisementData); 67 | pAdvertising->setScanResponseData(oScanResponseData); 68 | 69 | } 70 | 71 | void setup() { 72 | 73 | 74 | Serial.begin(115200); 75 | gettimeofday(&now, NULL); 76 | 77 | Serial.printf("start ESP32 %d\n",bootcount++); 78 | 79 | Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); 80 | 81 | last = now.tv_sec; 82 | 83 | // Create the BLE Device 84 | BLEDevice::init(""); 85 | 86 | // Create the BLE Server 87 | // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage 88 | 89 | pAdvertising = BLEDevice::getAdvertising(); 90 | 91 | setBeacon(); 92 | // Start advertising 93 | pAdvertising->start(); 94 | Serial.println("Advertizing started..."); 95 | delay(100); 96 | pAdvertising->stop(); 97 | Serial.printf("enter deep sleep\n"); 98 | esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); 99 | Serial.printf("in deep sleep\n"); 100 | } 101 | 102 | void loop() { 103 | } 104 | -------------------------------------------------------------------------------- /examples/BLE_notify/BLE_notify.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Video: https://www.youtube.com/watch?v=oCMOYS71NIU 3 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp 4 | Ported to Arduino ESP32 by Evandro Copercini 5 | updated by chegewara 6 | 7 | Create a BLE server that, once we receive a connection, will send periodic notifications. 8 | The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b 9 | And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 10 | 11 | The design of creating the BLE server is: 12 | 1. Create a BLE Server 13 | 2. Create a BLE Service 14 | 3. Create a BLE Characteristic on the Service 15 | 4. Create a BLE Descriptor on the characteristic 16 | 5. Start the service. 17 | 6. Start advertising. 18 | 19 | A connect hander associated with the server starts a background task that performs notification 20 | every couple of seconds. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | BLEServer* pServer = NULL; 28 | BLECharacteristic* pCharacteristic = NULL; 29 | bool deviceConnected = false; 30 | bool oldDeviceConnected = false; 31 | uint32_t value = 0; 32 | 33 | // See the following for generating UUIDs: 34 | // https://www.uuidgenerator.net/ 35 | 36 | #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" 37 | #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" 38 | 39 | 40 | class MyServerCallbacks: public BLEServerCallbacks { 41 | void onConnect(BLEServer* pServer) { 42 | deviceConnected = true; 43 | }; 44 | 45 | void onDisconnect(BLEServer* pServer) { 46 | deviceConnected = false; 47 | } 48 | }; 49 | 50 | 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | 55 | // Create the BLE Device 56 | BLEDevice::init("ESP32"); 57 | 58 | // Create the BLE Server 59 | pServer = BLEDevice::createServer(); 60 | pServer->setCallbacks(new MyServerCallbacks()); 61 | 62 | // Create the BLE Service 63 | BLEService *pService = pServer->createService(SERVICE_UUID); 64 | 65 | // Create a BLE Characteristic 66 | pCharacteristic = pService->createCharacteristic( 67 | CHARACTERISTIC_UUID, 68 | BLECharacteristic::PROPERTY_READ | 69 | BLECharacteristic::PROPERTY_WRITE | 70 | BLECharacteristic::PROPERTY_NOTIFY | 71 | BLECharacteristic::PROPERTY_INDICATE 72 | ); 73 | 74 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml 75 | // Create a BLE Descriptor 76 | pCharacteristic->addDescriptor(new BLE2902()); 77 | 78 | // Start the service 79 | pService->start(); 80 | 81 | // Start advertising 82 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); 83 | pAdvertising->addServiceUUID(SERVICE_UUID); 84 | pAdvertising->setScanResponse(false); 85 | pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter 86 | BLEDevice::startAdvertising(); 87 | Serial.println("Waiting a client connection to notify..."); 88 | } 89 | 90 | void loop() { 91 | // notify changed value 92 | if (deviceConnected) { 93 | pCharacteristic->setValue((uint8_t*)&value, 4); 94 | pCharacteristic->notify(); 95 | value++; 96 | delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms 97 | } 98 | // disconnecting 99 | if (!deviceConnected && oldDeviceConnected) { 100 | delay(500); // give the bluetooth stack the chance to get things ready 101 | pServer->startAdvertising(); // restart advertising 102 | Serial.println("start advertising"); 103 | oldDeviceConnected = deviceConnected; 104 | } 105 | // connecting 106 | if (deviceConnected && !oldDeviceConnected) { 107 | // do stuff here on connecting 108 | oldDeviceConnected = deviceConnected; 109 | } 110 | } -------------------------------------------------------------------------------- /examples/BLE_scan/BLE_scan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp 3 | Ported to Arduino ESP32 by Evandro Copercini 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int scanTime = 5; //In seconds 12 | BLEScan* pBLEScan; 13 | 14 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 15 | void onResult(BLEAdvertisedDevice advertisedDevice) { 16 | Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); 17 | } 18 | }; 19 | 20 | void setup() { 21 | Serial.begin(115200); 22 | Serial.println("Scanning..."); 23 | 24 | BLEDevice::init(""); 25 | pBLEScan = BLEDevice::getScan(); //create new scan 26 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 27 | pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster 28 | pBLEScan->setInterval(100); 29 | pBLEScan->setWindow(99); // less or equal setInterval value 30 | } 31 | 32 | void loop() { 33 | // put your main code here, to run repeatedly: 34 | BLEScanResults foundDevices = pBLEScan->start(scanTime, false); 35 | Serial.print("Devices found: "); 36 | Serial.println(foundDevices.getCount()); 37 | Serial.println("Scan done!"); 38 | pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory 39 | delay(2000); 40 | } -------------------------------------------------------------------------------- /examples/BLE_server/BLE_server.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp 3 | Ported to Arduino ESP32 by Evandro Copercini 4 | updates by chegewara 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | // See the following for generating UUIDs: 12 | // https://www.uuidgenerator.net/ 13 | 14 | #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" 15 | #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" 16 | 17 | void setup() { 18 | Serial.begin(115200); 19 | Serial.println("Starting BLE work!"); 20 | 21 | BLEDevice::init("Long name works now"); 22 | BLEServer *pServer = BLEDevice::createServer(); 23 | BLEService *pService = pServer->createService(SERVICE_UUID); 24 | BLECharacteristic *pCharacteristic = pService->createCharacteristic( 25 | CHARACTERISTIC_UUID, 26 | BLECharacteristic::PROPERTY_READ | 27 | BLECharacteristic::PROPERTY_WRITE 28 | ); 29 | 30 | pCharacteristic->setValue("Hello World says Neil"); 31 | pService->start(); 32 | // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility 33 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); 34 | pAdvertising->addServiceUUID(SERVICE_UUID); 35 | pAdvertising->setScanResponse(true); 36 | pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue 37 | pAdvertising->setMinPreferred(0x12); 38 | BLEDevice::startAdvertising(); 39 | Serial.println("Characteristic defined! Now you can read it in your phone!"); 40 | } 41 | 42 | void loop() { 43 | // put your main code here, to run repeatedly: 44 | delay(2000); 45 | } -------------------------------------------------------------------------------- /examples/BLE_server_multiconnect/BLE_server_multiconnect.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Video: https://www.youtube.com/watch?v=oCMOYS71NIU 3 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp 4 | Ported to Arduino ESP32 by Evandro Copercini 5 | updated by chegewara 6 | 7 | Create a BLE server that, once we receive a connection, will send periodic notifications. 8 | The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b 9 | And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 10 | 11 | The design of creating the BLE server is: 12 | 1. Create a BLE Server 13 | 2. Create a BLE Service 14 | 3. Create a BLE Characteristic on the Service 15 | 4. Create a BLE Descriptor on the characteristic 16 | 5. Start the service. 17 | 6. Start advertising. 18 | 19 | A connect hander associated with the server starts a background task that performs notification 20 | every couple of seconds. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | BLEServer* pServer = NULL; 28 | BLECharacteristic* pCharacteristic = NULL; 29 | bool deviceConnected = false; 30 | bool oldDeviceConnected = false; 31 | uint32_t value = 0; 32 | 33 | // See the following for generating UUIDs: 34 | // https://www.uuidgenerator.net/ 35 | 36 | #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" 37 | #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" 38 | 39 | 40 | class MyServerCallbacks: public BLEServerCallbacks { 41 | void onConnect(BLEServer* pServer) { 42 | deviceConnected = true; 43 | BLEDevice::startAdvertising(); 44 | }; 45 | 46 | void onDisconnect(BLEServer* pServer) { 47 | deviceConnected = false; 48 | } 49 | }; 50 | 51 | 52 | 53 | void setup() { 54 | Serial.begin(115200); 55 | 56 | // Create the BLE Device 57 | BLEDevice::init("ESP32"); 58 | 59 | // Create the BLE Server 60 | pServer = BLEDevice::createServer(); 61 | pServer->setCallbacks(new MyServerCallbacks()); 62 | 63 | // Create the BLE Service 64 | BLEService *pService = pServer->createService(SERVICE_UUID); 65 | 66 | // Create a BLE Characteristic 67 | pCharacteristic = pService->createCharacteristic( 68 | CHARACTERISTIC_UUID, 69 | BLECharacteristic::PROPERTY_READ | 70 | BLECharacteristic::PROPERTY_WRITE | 71 | BLECharacteristic::PROPERTY_NOTIFY | 72 | BLECharacteristic::PROPERTY_INDICATE 73 | ); 74 | 75 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml 76 | // Create a BLE Descriptor 77 | pCharacteristic->addDescriptor(new BLE2902()); 78 | 79 | // Start the service 80 | pService->start(); 81 | 82 | // Start advertising 83 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); 84 | pAdvertising->addServiceUUID(SERVICE_UUID); 85 | pAdvertising->setScanResponse(false); 86 | pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter 87 | BLEDevice::startAdvertising(); 88 | Serial.println("Waiting a client connection to notify..."); 89 | } 90 | 91 | void loop() { 92 | // notify changed value 93 | if (deviceConnected) { 94 | pCharacteristic->setValue((uint8_t*)&value, 4); 95 | pCharacteristic->notify(); 96 | value++; 97 | delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms 98 | } 99 | // disconnecting 100 | if (!deviceConnected && oldDeviceConnected) { 101 | delay(500); // give the bluetooth stack the chance to get things ready 102 | pServer->startAdvertising(); // restart advertising 103 | Serial.println("start advertising"); 104 | oldDeviceConnected = deviceConnected; 105 | } 106 | // connecting 107 | if (deviceConnected && !oldDeviceConnected) { 108 | // do stuff here on connecting 109 | oldDeviceConnected = deviceConnected; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /examples/BLE_uart/BLE_uart.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Video: https://www.youtube.com/watch?v=oCMOYS71NIU 3 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp 4 | Ported to Arduino ESP32 by Evandro Copercini 5 | 6 | Create a BLE server that, once we receive a connection, will send periodic notifications. 7 | The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E 8 | Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" 9 | Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" 10 | 11 | The design of creating the BLE server is: 12 | 1. Create a BLE Server 13 | 2. Create a BLE Service 14 | 3. Create a BLE Characteristic on the Service 15 | 4. Create a BLE Descriptor on the characteristic 16 | 5. Start the service. 17 | 6. Start advertising. 18 | 19 | In this example rxValue is the data received (only accessible inside that function). 20 | And txValue is the data to be sent, in this example just a byte incremented every second. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | BLEServer *pServer = NULL; 28 | BLECharacteristic * pTxCharacteristic; 29 | bool deviceConnected = false; 30 | bool oldDeviceConnected = false; 31 | uint8_t txValue = 0; 32 | 33 | // See the following for generating UUIDs: 34 | // https://www.uuidgenerator.net/ 35 | 36 | #define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID 37 | #define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" 38 | #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" 39 | 40 | 41 | class MyServerCallbacks: public BLEServerCallbacks { 42 | void onConnect(BLEServer* pServer) { 43 | deviceConnected = true; 44 | }; 45 | 46 | void onDisconnect(BLEServer* pServer) { 47 | deviceConnected = false; 48 | } 49 | }; 50 | 51 | class MyCallbacks: public BLECharacteristicCallbacks { 52 | void onWrite(BLECharacteristic *pCharacteristic) { 53 | std::string rxValue = pCharacteristic->getValue(); 54 | 55 | if (rxValue.length() > 0) { 56 | Serial.println("*********"); 57 | Serial.print("Received Value: "); 58 | for (int i = 0; i < rxValue.length(); i++) 59 | Serial.print(rxValue[i]); 60 | 61 | Serial.println(); 62 | Serial.println("*********"); 63 | } 64 | } 65 | }; 66 | 67 | 68 | void setup() { 69 | Serial.begin(115200); 70 | 71 | // Create the BLE Device 72 | BLEDevice::init("UART Service"); 73 | 74 | // Create the BLE Server 75 | pServer = BLEDevice::createServer(); 76 | pServer->setCallbacks(new MyServerCallbacks()); 77 | 78 | // Create the BLE Service 79 | BLEService *pService = pServer->createService(SERVICE_UUID); 80 | 81 | // Create a BLE Characteristic 82 | pTxCharacteristic = pService->createCharacteristic( 83 | CHARACTERISTIC_UUID_TX, 84 | BLECharacteristic::PROPERTY_NOTIFY 85 | ); 86 | 87 | pTxCharacteristic->addDescriptor(new BLE2902()); 88 | 89 | BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( 90 | CHARACTERISTIC_UUID_RX, 91 | BLECharacteristic::PROPERTY_WRITE 92 | ); 93 | 94 | pRxCharacteristic->setCallbacks(new MyCallbacks()); 95 | 96 | // Start the service 97 | pService->start(); 98 | 99 | // Start advertising 100 | pServer->getAdvertising()->start(); 101 | Serial.println("Waiting a client connection to notify..."); 102 | } 103 | 104 | void loop() { 105 | 106 | if (deviceConnected) { 107 | pTxCharacteristic->setValue(&txValue, 1); 108 | pTxCharacteristic->notify(); 109 | txValue++; 110 | delay(10); // bluetooth stack will go into congestion, if too many packets are sent 111 | } 112 | 113 | // disconnecting 114 | if (!deviceConnected && oldDeviceConnected) { 115 | delay(500); // give the bluetooth stack the chance to get things ready 116 | pServer->startAdvertising(); // restart advertising 117 | Serial.println("start advertising"); 118 | oldDeviceConnected = deviceConnected; 119 | } 120 | // connecting 121 | if (deviceConnected && !oldDeviceConnected) { 122 | // do stuff here on connecting 123 | oldDeviceConnected = deviceConnected; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /examples/BLE_write/BLE_write.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp 3 | Ported to Arduino ESP32 by Evandro Copercini 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // See the following for generating UUIDs: 11 | // https://www.uuidgenerator.net/ 12 | 13 | #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" 14 | #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" 15 | 16 | 17 | class MyCallbacks: public BLECharacteristicCallbacks { 18 | void onWrite(BLECharacteristic *pCharacteristic) { 19 | std::string value = pCharacteristic->getValue(); 20 | 21 | if (value.length() > 0) { 22 | Serial.println("*********"); 23 | Serial.print("New value: "); 24 | for (int i = 0; i < value.length(); i++) 25 | Serial.print(value[i]); 26 | 27 | Serial.println(); 28 | Serial.println("*********"); 29 | } 30 | } 31 | }; 32 | 33 | void setup() { 34 | Serial.begin(115200); 35 | 36 | Serial.println("1- Download and install an BLE scanner app in your phone"); 37 | Serial.println("2- Scan for BLE devices in the app"); 38 | Serial.println("3- Connect to MyESP32"); 39 | Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); 40 | Serial.println("5- See the magic =)"); 41 | 42 | BLEDevice::init("MyESP32"); 43 | BLEServer *pServer = BLEDevice::createServer(); 44 | 45 | BLEService *pService = pServer->createService(SERVICE_UUID); 46 | 47 | BLECharacteristic *pCharacteristic = pService->createCharacteristic( 48 | CHARACTERISTIC_UUID, 49 | BLECharacteristic::PROPERTY_READ | 50 | BLECharacteristic::PROPERTY_WRITE 51 | ); 52 | 53 | pCharacteristic->setCallbacks(new MyCallbacks()); 54 | 55 | pCharacteristic->setValue("Hello World"); 56 | pService->start(); 57 | 58 | BLEAdvertising *pAdvertising = pServer->getAdvertising(); 59 | pAdvertising->start(); 60 | } 61 | 62 | void loop() { 63 | // put your main code here, to run repeatedly: 64 | delay(2000); 65 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32 BLE Arduino 2 | version=1.0.1 3 | author=Neil Kolban 4 | maintainer=Dariusz Krempa 5 | sentence=BLE functions for ESP32 6 | paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform. 7 | category=Communication 8 | url=https://github.com/nkolban/ESP32_BLE_Arduino 9 | architectures=esp32 10 | includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h 11 | -------------------------------------------------------------------------------- /src/BLE2902.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLE2902.cpp 3 | * 4 | * Created on: Jun 25, 2017 5 | * Author: kolban 6 | */ 7 | 8 | /* 9 | * See also: 10 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml 11 | */ 12 | #include "sdkconfig.h" 13 | #if defined(CONFIG_BT_ENABLED) 14 | 15 | #include "BLE2902.h" 16 | 17 | BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) { 18 | uint8_t data[2] = { 0, 0 }; 19 | setValue(data, 2); 20 | } // BLE2902 21 | 22 | 23 | /** 24 | * @brief Get the notifications value. 25 | * @return The notifications value. True if notifications are enabled and false if not. 26 | */ 27 | bool BLE2902::getNotifications() { 28 | return (getValue()[0] & (1 << 0)) != 0; 29 | } // getNotifications 30 | 31 | 32 | /** 33 | * @brief Get the indications value. 34 | * @return The indications value. True if indications are enabled and false if not. 35 | */ 36 | bool BLE2902::getIndications() { 37 | return (getValue()[0] & (1 << 1)) != 0; 38 | } // getIndications 39 | 40 | 41 | /** 42 | * @brief Set the indications flag. 43 | * @param [in] flag The indications flag. 44 | */ 45 | void BLE2902::setIndications(bool flag) { 46 | uint8_t *pValue = getValue(); 47 | if (flag) pValue[0] |= 1 << 1; 48 | else pValue[0] &= ~(1 << 1); 49 | } // setIndications 50 | 51 | 52 | /** 53 | * @brief Set the notifications flag. 54 | * @param [in] flag The notifications flag. 55 | */ 56 | void BLE2902::setNotifications(bool flag) { 57 | uint8_t *pValue = getValue(); 58 | if (flag) pValue[0] |= 1 << 0; 59 | else pValue[0] &= ~(1 << 0); 60 | } // setNotifications 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/BLE2902.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLE2902.h 3 | * 4 | * Created on: Jun 25, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLE2902_H_ 9 | #define COMPONENTS_CPP_UTILS_BLE2902_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include "BLEDescriptor.h" 14 | 15 | /** 16 | * @brief Descriptor for Client Characteristic Configuration. 17 | * 18 | * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902. 19 | * 20 | * See also: 21 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml 22 | */ 23 | class BLE2902: public BLEDescriptor { 24 | public: 25 | BLE2902(); 26 | bool getNotifications(); 27 | bool getIndications(); 28 | void setNotifications(bool flag); 29 | void setIndications(bool flag); 30 | 31 | }; // BLE2902 32 | 33 | #endif /* CONFIG_BT_ENABLED */ 34 | #endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ 35 | -------------------------------------------------------------------------------- /src/BLE2904.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLE2904.cpp 3 | * 4 | * Created on: Dec 23, 2017 5 | * Author: kolban 6 | */ 7 | 8 | /* 9 | * See also: 10 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml 11 | */ 12 | #include "sdkconfig.h" 13 | #if defined(CONFIG_BT_ENABLED) 14 | 15 | #include "BLE2904.h" 16 | 17 | 18 | BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) { 19 | m_data.m_format = 0; 20 | m_data.m_exponent = 0; 21 | m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers 22 | m_data.m_unit = 0; 23 | m_data.m_description = 0; 24 | setValue((uint8_t*) &m_data, sizeof(m_data)); 25 | } // BLE2902 26 | 27 | 28 | /** 29 | * @brief Set the description. 30 | */ 31 | void BLE2904::setDescription(uint16_t description) { 32 | m_data.m_description = description; 33 | setValue((uint8_t*) &m_data, sizeof(m_data)); 34 | } 35 | 36 | 37 | /** 38 | * @brief Set the exponent. 39 | */ 40 | void BLE2904::setExponent(int8_t exponent) { 41 | m_data.m_exponent = exponent; 42 | setValue((uint8_t*) &m_data, sizeof(m_data)); 43 | } // setExponent 44 | 45 | 46 | /** 47 | * @brief Set the format. 48 | */ 49 | void BLE2904::setFormat(uint8_t format) { 50 | m_data.m_format = format; 51 | setValue((uint8_t*) &m_data, sizeof(m_data)); 52 | } // setFormat 53 | 54 | 55 | /** 56 | * @brief Set the namespace. 57 | */ 58 | void BLE2904::setNamespace(uint8_t namespace_value) { 59 | m_data.m_namespace = namespace_value; 60 | setValue((uint8_t*) &m_data, sizeof(m_data)); 61 | } // setNamespace 62 | 63 | 64 | /** 65 | * @brief Set the units for this value. It should be one of the encoded values defined here: 66 | * https://www.bluetooth.com/specifications/assigned-numbers/units 67 | * @param [in] unit The type of units of this characteristic as defined by assigned numbers. 68 | */ 69 | void BLE2904::setUnit(uint16_t unit) { 70 | m_data.m_unit = unit; 71 | setValue((uint8_t*) &m_data, sizeof(m_data)); 72 | } // setUnit 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/BLE2904.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLE2904.h 3 | * 4 | * Created on: Dec 23, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLE2904_H_ 9 | #define COMPONENTS_CPP_UTILS_BLE2904_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include "BLEDescriptor.h" 14 | 15 | struct BLE2904_Data { 16 | uint8_t m_format; 17 | int8_t m_exponent; 18 | uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units 19 | uint8_t m_namespace; 20 | uint16_t m_description; 21 | 22 | } __attribute__((packed)); 23 | 24 | /** 25 | * @brief Descriptor for Characteristic Presentation Format. 26 | * 27 | * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. 28 | * 29 | * See also: 30 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml 31 | */ 32 | class BLE2904: public BLEDescriptor { 33 | public: 34 | BLE2904(); 35 | static const uint8_t FORMAT_BOOLEAN = 1; 36 | static const uint8_t FORMAT_UINT2 = 2; 37 | static const uint8_t FORMAT_UINT4 = 3; 38 | static const uint8_t FORMAT_UINT8 = 4; 39 | static const uint8_t FORMAT_UINT12 = 5; 40 | static const uint8_t FORMAT_UINT16 = 6; 41 | static const uint8_t FORMAT_UINT24 = 7; 42 | static const uint8_t FORMAT_UINT32 = 8; 43 | static const uint8_t FORMAT_UINT48 = 9; 44 | static const uint8_t FORMAT_UINT64 = 10; 45 | static const uint8_t FORMAT_UINT128 = 11; 46 | static const uint8_t FORMAT_SINT8 = 12; 47 | static const uint8_t FORMAT_SINT12 = 13; 48 | static const uint8_t FORMAT_SINT16 = 14; 49 | static const uint8_t FORMAT_SINT24 = 15; 50 | static const uint8_t FORMAT_SINT32 = 16; 51 | static const uint8_t FORMAT_SINT48 = 17; 52 | static const uint8_t FORMAT_SINT64 = 18; 53 | static const uint8_t FORMAT_SINT128 = 19; 54 | static const uint8_t FORMAT_FLOAT32 = 20; 55 | static const uint8_t FORMAT_FLOAT64 = 21; 56 | static const uint8_t FORMAT_SFLOAT16 = 22; 57 | static const uint8_t FORMAT_SFLOAT32 = 23; 58 | static const uint8_t FORMAT_IEEE20601 = 24; 59 | static const uint8_t FORMAT_UTF8 = 25; 60 | static const uint8_t FORMAT_UTF16 = 26; 61 | static const uint8_t FORMAT_OPAQUE = 27; 62 | 63 | void setDescription(uint16_t); 64 | void setExponent(int8_t exponent); 65 | void setFormat(uint8_t format); 66 | void setNamespace(uint8_t namespace_value); 67 | void setUnit(uint16_t unit); 68 | 69 | private: 70 | BLE2904_Data m_data; 71 | }; // BLE2904 72 | 73 | #endif /* CONFIG_BT_ENABLED */ 74 | #endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ 75 | -------------------------------------------------------------------------------- /src/BLEAddress.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEAddress.cpp 3 | * 4 | * Created on: Jul 2, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | 10 | #include "BLEAddress.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #ifdef ARDUINO_ARCH_ESP32 17 | #include "esp32-hal-log.h" 18 | #endif 19 | 20 | 21 | /** 22 | * @brief Create an address from the native ESP32 representation. 23 | * @param [in] address The native representation. 24 | */ 25 | BLEAddress::BLEAddress(esp_bd_addr_t address) { 26 | memcpy(m_address, address, ESP_BD_ADDR_LEN); 27 | } // BLEAddress 28 | 29 | 30 | /** 31 | * @brief Create an address from a hex string 32 | * 33 | * A hex string is of the format: 34 | * ``` 35 | * 00:00:00:00:00:00 36 | * ``` 37 | * which is 17 characters in length. 38 | * 39 | * @param [in] stringAddress The hex representation of the address. 40 | */ 41 | BLEAddress::BLEAddress(std::string stringAddress) { 42 | if (stringAddress.length() != 17) return; 43 | 44 | int data[6]; 45 | sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]); 46 | m_address[0] = (uint8_t) data[0]; 47 | m_address[1] = (uint8_t) data[1]; 48 | m_address[2] = (uint8_t) data[2]; 49 | m_address[3] = (uint8_t) data[3]; 50 | m_address[4] = (uint8_t) data[4]; 51 | m_address[5] = (uint8_t) data[5]; 52 | } // BLEAddress 53 | 54 | 55 | /** 56 | * @brief Determine if this address equals another. 57 | * @param [in] otherAddress The other address to compare against. 58 | * @return True if the addresses are equal. 59 | */ 60 | bool BLEAddress::equals(BLEAddress otherAddress) { 61 | return memcmp(otherAddress.getNative(), m_address, 6) == 0; 62 | } // equals 63 | 64 | 65 | /** 66 | * @brief Return the native representation of the address. 67 | * @return The native representation of the address. 68 | */ 69 | esp_bd_addr_t *BLEAddress::getNative() { 70 | return &m_address; 71 | } // getNative 72 | 73 | 74 | /** 75 | * @brief Convert a BLE address to a string. 76 | * 77 | * A string representation of an address is in the format: 78 | * 79 | * ``` 80 | * xx:xx:xx:xx:xx:xx 81 | * ``` 82 | * 83 | * @return The string representation of the address. 84 | */ 85 | std::string BLEAddress::toString() { 86 | std::stringstream stream; 87 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[0] << ':'; 88 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[1] << ':'; 89 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[2] << ':'; 90 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[3] << ':'; 91 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[4] << ':'; 92 | stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[5]; 93 | return stream.str(); 94 | } // toString 95 | #endif 96 | -------------------------------------------------------------------------------- /src/BLEAddress.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEAddress.h 3 | * 4 | * Created on: Jul 2, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEADDRESS_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include // ESP32 BLE 13 | #include 14 | 15 | 16 | /** 17 | * @brief A %BLE device address. 18 | * 19 | * Every %BLE device has a unique address which can be used to identify it and form connections. 20 | */ 21 | class BLEAddress { 22 | public: 23 | BLEAddress(esp_bd_addr_t address); 24 | BLEAddress(std::string stringAddress); 25 | bool equals(BLEAddress otherAddress); 26 | esp_bd_addr_t* getNative(); 27 | std::string toString(); 28 | 29 | private: 30 | esp_bd_addr_t m_address; 31 | }; 32 | 33 | #endif /* CONFIG_BT_ENABLED */ 34 | #endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */ 35 | -------------------------------------------------------------------------------- /src/BLEAdvertisedDevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEAdvertisedDevice.h 3 | * 4 | * Created on: Jul 3, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | 14 | #include 15 | 16 | #include "BLEAddress.h" 17 | #include "BLEScan.h" 18 | #include "BLEUUID.h" 19 | 20 | 21 | class BLEScan; 22 | /** 23 | * @brief A representation of a %BLE advertised device found by a scan. 24 | * 25 | * When we perform a %BLE scan, the result will be a set of devices that are advertising. This 26 | * class provides a model of a detected device. 27 | */ 28 | class BLEAdvertisedDevice { 29 | public: 30 | BLEAdvertisedDevice(); 31 | 32 | BLEAddress getAddress(); 33 | uint16_t getAppearance(); 34 | std::string getManufacturerData(); 35 | std::string getName(); 36 | int getRSSI(); 37 | BLEScan* getScan(); 38 | std::string getServiceData(); 39 | BLEUUID getServiceDataUUID(); 40 | BLEUUID getServiceUUID(); 41 | int8_t getTXPower(); 42 | uint8_t* getPayload(); 43 | size_t getPayloadLength(); 44 | esp_ble_addr_type_t getAddressType(); 45 | void setAddressType(esp_ble_addr_type_t type); 46 | 47 | 48 | bool isAdvertisingService(BLEUUID uuid); 49 | bool haveAppearance(); 50 | bool haveManufacturerData(); 51 | bool haveName(); 52 | bool haveRSSI(); 53 | bool haveServiceData(); 54 | bool haveServiceUUID(); 55 | bool haveTXPower(); 56 | 57 | std::string toString(); 58 | 59 | private: 60 | friend class BLEScan; 61 | 62 | void parseAdvertisement(uint8_t* payload, size_t total_len=62); 63 | void setAddress(BLEAddress address); 64 | void setAdFlag(uint8_t adFlag); 65 | void setAdvertizementResult(uint8_t* payload); 66 | void setAppearance(uint16_t appearance); 67 | void setManufacturerData(std::string manufacturerData); 68 | void setName(std::string name); 69 | void setRSSI(int rssi); 70 | void setScan(BLEScan* pScan); 71 | void setServiceData(std::string data); 72 | void setServiceDataUUID(BLEUUID uuid); 73 | void setServiceUUID(const char* serviceUUID); 74 | void setServiceUUID(BLEUUID serviceUUID); 75 | void setTXPower(int8_t txPower); 76 | 77 | bool m_haveAppearance; 78 | bool m_haveManufacturerData; 79 | bool m_haveName; 80 | bool m_haveRSSI; 81 | bool m_haveServiceData; 82 | bool m_haveServiceUUID; 83 | bool m_haveTXPower; 84 | 85 | 86 | BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); 87 | uint8_t m_adFlag; 88 | uint16_t m_appearance; 89 | int m_deviceType; 90 | std::string m_manufacturerData; 91 | std::string m_name; 92 | BLEScan* m_pScan; 93 | int m_rssi; 94 | std::vector m_serviceUUIDs; 95 | int8_t m_txPower; 96 | std::string m_serviceData; 97 | BLEUUID m_serviceDataUUID; 98 | uint8_t* m_payload; 99 | size_t m_payloadLength = 0; 100 | esp_ble_addr_type_t m_addressType; 101 | }; 102 | 103 | /** 104 | * @brief A callback handler for callbacks associated device scanning. 105 | * 106 | * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising 107 | * has been found. This class can be sub-classed and registered such that when a scan is performed and 108 | * a new advertised device has been found, we will be called back to be notified. 109 | */ 110 | class BLEAdvertisedDeviceCallbacks { 111 | public: 112 | virtual ~BLEAdvertisedDeviceCallbacks() {} 113 | /** 114 | * @brief Called when a new scan result is detected. 115 | * 116 | * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the 117 | * device that was found. During any individual scan, a device will only be detected one time. 118 | */ 119 | virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0; 120 | }; 121 | 122 | #endif /* CONFIG_BT_ENABLED */ 123 | #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */ 124 | -------------------------------------------------------------------------------- /src/BLEAdvertising.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEAdvertising.h 3 | * 4 | * Created on: Jun 21, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | #include "BLEUUID.h" 14 | #include 15 | #include "FreeRTOS.h" 16 | 17 | /** 18 | * @brief Advertisement data set by the programmer to be published by the %BLE server. 19 | */ 20 | class BLEAdvertisementData { 21 | // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will 22 | // be exposed on demand/request or as time permits. 23 | // 24 | public: 25 | void setAppearance(uint16_t appearance); 26 | void setCompleteServices(BLEUUID uuid); 27 | void setFlags(uint8_t); 28 | void setManufacturerData(std::string data); 29 | void setName(std::string name); 30 | void setPartialServices(BLEUUID uuid); 31 | void setServiceData(BLEUUID uuid, std::string data); 32 | void setShortName(std::string name); 33 | void addData(std::string data); // Add data to the payload. 34 | std::string getPayload(); // Retrieve the current advert payload. 35 | 36 | private: 37 | friend class BLEAdvertising; 38 | std::string m_payload; // The payload of the advertisement. 39 | }; // BLEAdvertisementData 40 | 41 | 42 | /** 43 | * @brief Perform and manage %BLE advertising. 44 | * 45 | * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. 46 | */ 47 | class BLEAdvertising { 48 | public: 49 | BLEAdvertising(); 50 | void addServiceUUID(BLEUUID serviceUUID); 51 | void addServiceUUID(const char* serviceUUID); 52 | void start(); 53 | void stop(); 54 | void setAppearance(uint16_t appearance); 55 | void setMaxInterval(uint16_t maxinterval); 56 | void setMinInterval(uint16_t mininterval); 57 | void setAdvertisementData(BLEAdvertisementData& advertisementData); 58 | void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); 59 | void setScanResponseData(BLEAdvertisementData& advertisementData); 60 | void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM); 61 | 62 | void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); 63 | void setMinPreferred(uint16_t); 64 | void setMaxPreferred(uint16_t); 65 | void setScanResponse(bool); 66 | 67 | private: 68 | esp_ble_adv_data_t m_advData; 69 | esp_ble_adv_params_t m_advParams; 70 | std::vector m_serviceUUIDs; 71 | bool m_customAdvData = false; // Are we using custom advertising data? 72 | bool m_customScanResponseData = false; // Are we using custom scan response data? 73 | FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); 74 | bool m_scanResp = true; 75 | 76 | }; 77 | #endif /* CONFIG_BT_ENABLED */ 78 | #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ -------------------------------------------------------------------------------- /src/BLEBeacon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEBeacon.cpp 3 | * 4 | * Created on: Jan 4, 2018 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include "BLEBeacon.h" 11 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 12 | #include "esp32-hal-log.h" 13 | #define LOG_TAG "" 14 | #else 15 | #include "esp_log.h" 16 | static const char* LOG_TAG = "BLEBeacon"; 17 | #endif 18 | 19 | #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) 20 | 21 | 22 | BLEBeacon::BLEBeacon() { 23 | m_beaconData.manufacturerId = 0x4c00; 24 | m_beaconData.subType = 0x02; 25 | m_beaconData.subTypeLength = 0x15; 26 | m_beaconData.major = 0; 27 | m_beaconData.minor = 0; 28 | m_beaconData.signalPower = 0; 29 | memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); 30 | } // BLEBeacon 31 | 32 | std::string BLEBeacon::getData() { 33 | return std::string((char*) &m_beaconData, sizeof(m_beaconData)); 34 | } // getData 35 | 36 | uint16_t BLEBeacon::getMajor() { 37 | return m_beaconData.major; 38 | } 39 | 40 | uint16_t BLEBeacon::getManufacturerId() { 41 | return m_beaconData.manufacturerId; 42 | } 43 | 44 | uint16_t BLEBeacon::getMinor() { 45 | return m_beaconData.minor; 46 | } 47 | 48 | BLEUUID BLEBeacon::getProximityUUID() { 49 | return BLEUUID(m_beaconData.proximityUUID, 16, false); 50 | } 51 | 52 | int8_t BLEBeacon::getSignalPower() { 53 | return m_beaconData.signalPower; 54 | } 55 | 56 | /** 57 | * Set the raw data for the beacon record. 58 | */ 59 | void BLEBeacon::setData(std::string data) { 60 | if (data.length() != sizeof(m_beaconData)) { 61 | ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); 62 | return; 63 | } 64 | memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); 65 | } // setData 66 | 67 | void BLEBeacon::setMajor(uint16_t major) { 68 | m_beaconData.major = ENDIAN_CHANGE_U16(major); 69 | } // setMajor 70 | 71 | void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { 72 | m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); 73 | } // setManufacturerId 74 | 75 | void BLEBeacon::setMinor(uint16_t minor) { 76 | m_beaconData.minor = ENDIAN_CHANGE_U16(minor); 77 | } // setMinior 78 | 79 | void BLEBeacon::setProximityUUID(BLEUUID uuid) { 80 | uuid = uuid.to128(); 81 | memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); 82 | } // setProximityUUID 83 | 84 | void BLEBeacon::setSignalPower(int8_t signalPower) { 85 | m_beaconData.signalPower = signalPower; 86 | } // setSignalPower 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/BLEBeacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEBeacon2.h 3 | * 4 | * Created on: Jan 4, 2018 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEBEACON_H_ 10 | #include "BLEUUID.h" 11 | /** 12 | * @brief Representation of a beacon. 13 | * See: 14 | * * https://en.wikipedia.org/wiki/IBeacon 15 | */ 16 | class BLEBeacon { 17 | private: 18 | struct { 19 | uint16_t manufacturerId; 20 | uint8_t subType; 21 | uint8_t subTypeLength; 22 | uint8_t proximityUUID[16]; 23 | uint16_t major; 24 | uint16_t minor; 25 | int8_t signalPower; 26 | } __attribute__((packed)) m_beaconData; 27 | public: 28 | BLEBeacon(); 29 | std::string getData(); 30 | uint16_t getMajor(); 31 | uint16_t getMinor(); 32 | uint16_t getManufacturerId(); 33 | BLEUUID getProximityUUID(); 34 | int8_t getSignalPower(); 35 | void setData(std::string data); 36 | void setMajor(uint16_t major); 37 | void setMinor(uint16_t minor); 38 | void setManufacturerId(uint16_t manufacturerId); 39 | void setProximityUUID(BLEUUID uuid); 40 | void setSignalPower(int8_t signalPower); 41 | }; // BLEBeacon 42 | 43 | #endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ 44 | -------------------------------------------------------------------------------- /src/BLECharacteristic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLECharacteristic.h 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ 9 | #define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | #include 14 | #include "BLEUUID.h" 15 | #include 16 | #include 17 | #include "BLEDescriptor.h" 18 | #include "BLEValue.h" 19 | #include "FreeRTOS.h" 20 | 21 | class BLEService; 22 | class BLEDescriptor; 23 | class BLECharacteristicCallbacks; 24 | 25 | /** 26 | * @brief A management structure for %BLE descriptors. 27 | */ 28 | class BLEDescriptorMap { 29 | public: 30 | void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); 31 | void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); 32 | void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); 33 | BLEDescriptor* getByUUID(const char* uuid); 34 | BLEDescriptor* getByUUID(BLEUUID uuid); 35 | BLEDescriptor* getByHandle(uint16_t handle); 36 | std::string toString(); 37 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); 38 | BLEDescriptor* getFirst(); 39 | BLEDescriptor* getNext(); 40 | private: 41 | std::map m_uuidMap; 42 | std::map m_handleMap; 43 | std::map::iterator m_iterator; 44 | }; 45 | 46 | 47 | /** 48 | * @brief The model of a %BLE Characteristic. 49 | * 50 | * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and 51 | * can be read and written to by a %BLE client. 52 | */ 53 | class BLECharacteristic { 54 | public: 55 | BLECharacteristic(const char* uuid, uint32_t properties = 0); 56 | BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); 57 | virtual ~BLECharacteristic(); 58 | 59 | void addDescriptor(BLEDescriptor* pDescriptor); 60 | BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); 61 | BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); 62 | BLEUUID getUUID(); 63 | std::string getValue(); 64 | uint8_t* getData(); 65 | 66 | void indicate(); 67 | void notify(bool is_notification = true); 68 | void setBroadcastProperty(bool value); 69 | void setCallbacks(BLECharacteristicCallbacks* pCallbacks); 70 | void setIndicateProperty(bool value); 71 | void setNotifyProperty(bool value); 72 | void setReadProperty(bool value); 73 | void setValue(uint8_t* data, size_t size); 74 | void setValue(std::string value); 75 | void setValue(uint16_t& data16); 76 | void setValue(uint32_t& data32); 77 | void setValue(int& data32); 78 | void setValue(float& data32); 79 | void setValue(double& data64); 80 | void setWriteProperty(bool value); 81 | void setWriteNoResponseProperty(bool value); 82 | std::string toString(); 83 | uint16_t getHandle(); 84 | void setAccessPermissions(esp_gatt_perm_t perm); 85 | 86 | static const uint32_t PROPERTY_READ = 1<<0; 87 | static const uint32_t PROPERTY_WRITE = 1<<1; 88 | static const uint32_t PROPERTY_NOTIFY = 1<<2; 89 | static const uint32_t PROPERTY_BROADCAST = 1<<3; 90 | static const uint32_t PROPERTY_INDICATE = 1<<4; 91 | static const uint32_t PROPERTY_WRITE_NR = 1<<5; 92 | 93 | private: 94 | 95 | friend class BLEServer; 96 | friend class BLEService; 97 | friend class BLEDescriptor; 98 | friend class BLECharacteristicMap; 99 | 100 | BLEUUID m_bleUUID; 101 | BLEDescriptorMap m_descriptorMap; 102 | uint16_t m_handle; 103 | esp_gatt_char_prop_t m_properties; 104 | BLECharacteristicCallbacks* m_pCallbacks; 105 | BLEService* m_pService; 106 | BLEValue m_value; 107 | esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; 108 | 109 | void handleGATTServerEvent( 110 | esp_gatts_cb_event_t event, 111 | esp_gatt_if_t gatts_if, 112 | esp_ble_gatts_cb_param_t* param); 113 | 114 | void executeCreate(BLEService* pService); 115 | esp_gatt_char_prop_t getProperties(); 116 | BLEService* getService(); 117 | void setHandle(uint16_t handle); 118 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 119 | FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); 120 | }; // BLECharacteristic 121 | 122 | 123 | /** 124 | * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. 125 | * 126 | * When a server application creates a %BLE characteristic, we may wish to be informed when there is either 127 | * a read or write request to the characteristic's value. An application can register a 128 | * sub-classed instance of this class and will be notified when such an event happens. 129 | */ 130 | class BLECharacteristicCallbacks { 131 | public: 132 | virtual ~BLECharacteristicCallbacks(); 133 | virtual void onRead(BLECharacteristic* pCharacteristic); 134 | virtual void onWrite(BLECharacteristic* pCharacteristic); 135 | }; 136 | #endif /* CONFIG_BT_ENABLED */ 137 | #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ 138 | -------------------------------------------------------------------------------- /src/BLECharacteristicMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLECharacteristicMap.cpp 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include "BLEService.h" 12 | #ifdef ARDUINO_ARCH_ESP32 13 | #include "esp32-hal-log.h" 14 | #endif 15 | 16 | 17 | /** 18 | * @brief Return the characteristic by handle. 19 | * @param [in] handle The handle to look up the characteristic. 20 | * @return The characteristic. 21 | */ 22 | BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { 23 | return m_handleMap.at(handle); 24 | } // getByHandle 25 | 26 | 27 | /** 28 | * @brief Return the characteristic by UUID. 29 | * @param [in] UUID The UUID to look up the characteristic. 30 | * @return The characteristic. 31 | */ 32 | BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) { 33 | return getByUUID(BLEUUID(uuid)); 34 | } 35 | 36 | 37 | /** 38 | * @brief Return the characteristic by UUID. 39 | * @param [in] UUID The UUID to look up the characteristic. 40 | * @return The characteristic. 41 | */ 42 | BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { 43 | for (auto &myPair : m_uuidMap) { 44 | if (myPair.first->getUUID().equals(uuid)) { 45 | return myPair.first; 46 | } 47 | } 48 | //return m_uuidMap.at(uuid.toString()); 49 | return nullptr; 50 | } // getByUUID 51 | 52 | 53 | /** 54 | * @brief Get the first characteristic in the map. 55 | * @return The first characteristic in the map. 56 | */ 57 | BLECharacteristic* BLECharacteristicMap::getFirst() { 58 | m_iterator = m_uuidMap.begin(); 59 | if (m_iterator == m_uuidMap.end()) return nullptr; 60 | BLECharacteristic* pRet = m_iterator->first; 61 | m_iterator++; 62 | return pRet; 63 | } // getFirst 64 | 65 | 66 | /** 67 | * @brief Get the next characteristic in the map. 68 | * @return The next characteristic in the map. 69 | */ 70 | BLECharacteristic* BLECharacteristicMap::getNext() { 71 | if (m_iterator == m_uuidMap.end()) return nullptr; 72 | BLECharacteristic* pRet = m_iterator->first; 73 | m_iterator++; 74 | return pRet; 75 | } // getNext 76 | 77 | 78 | /** 79 | * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping 80 | * @param [in] event 81 | * @param [in] gatts_if 82 | * @param [in] param 83 | */ 84 | void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { 85 | // Invoke the handler for every Service we have. 86 | for (auto& myPair : m_uuidMap) { 87 | myPair.first->handleGATTServerEvent(event, gatts_if, param); 88 | } 89 | } // handleGATTServerEvent 90 | 91 | 92 | /** 93 | * @brief Set the characteristic by handle. 94 | * @param [in] handle The handle of the characteristic. 95 | * @param [in] characteristic The characteristic to cache. 96 | * @return N/A. 97 | */ 98 | void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) { 99 | m_handleMap.insert(std::pair(handle, characteristic)); 100 | } // setByHandle 101 | 102 | 103 | /** 104 | * @brief Set the characteristic by UUID. 105 | * @param [in] uuid The uuid of the characteristic. 106 | * @param [in] characteristic The characteristic to cache. 107 | * @return N/A. 108 | */ 109 | void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) { 110 | m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); 111 | } // setByUUID 112 | 113 | 114 | /** 115 | * @brief Return a string representation of the characteristic map. 116 | * @return A string representation of the characteristic map. 117 | */ 118 | std::string BLECharacteristicMap::toString() { 119 | std::stringstream stringStream; 120 | stringStream << std::hex << std::setfill('0'); 121 | int count = 0; 122 | for (auto &myPair: m_uuidMap) { 123 | if (count > 0) { 124 | stringStream << "\n"; 125 | } 126 | count++; 127 | stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); 128 | } 129 | return stringStream.str(); 130 | } // toString 131 | 132 | 133 | #endif /* CONFIG_BT_ENABLED */ 134 | -------------------------------------------------------------------------------- /src/BLEClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEDevice.h 3 | * 4 | * Created on: Mar 22, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef MAIN_BLEDEVICE_H_ 9 | #define MAIN_BLEDEVICE_H_ 10 | 11 | #include "sdkconfig.h" 12 | #if defined(CONFIG_BT_ENABLED) 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "BLEExceptions.h" 19 | #include "BLERemoteService.h" 20 | #include "BLEService.h" 21 | #include "BLEAddress.h" 22 | #include "BLEAdvertisedDevice.h" 23 | 24 | class BLERemoteService; 25 | class BLEClientCallbacks; 26 | class BLEAdvertisedDevice; 27 | 28 | /** 29 | * @brief A model of a %BLE client. 30 | */ 31 | class BLEClient { 32 | public: 33 | BLEClient(); 34 | ~BLEClient(); 35 | 36 | bool connect(BLEAdvertisedDevice* device); 37 | bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server 38 | void disconnect(); // Disconnect from the remote BLE Server 39 | BLEAddress getPeerAddress(); // Get the address of the remote BLE Server 40 | int getRssi(); // Get the RSSI of the remote BLE Server 41 | std::map* getServices(); // Get a map of the services offered by the remote BLE Server 42 | BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server. 43 | BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server. 44 | std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service. 45 | 46 | 47 | void handleGAPEvent( 48 | esp_gap_ble_cb_event_t event, 49 | esp_ble_gap_cb_param_t* param); 50 | 51 | bool isConnected(); // Return true if we are connected. 52 | 53 | void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); 54 | void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. 55 | 56 | std::string toString(); // Return a string representation of this client. 57 | uint16_t getConnId(); 58 | esp_gatt_if_t getGattcIf(); 59 | uint16_t getMTU(); 60 | 61 | uint16_t m_appId; 62 | private: 63 | friend class BLEDevice; 64 | friend class BLERemoteService; 65 | friend class BLERemoteCharacteristic; 66 | friend class BLERemoteDescriptor; 67 | 68 | void gattClientEventHandler( 69 | esp_gattc_cb_event_t event, 70 | esp_gatt_if_t gattc_if, 71 | esp_ble_gattc_cb_param_t* param); 72 | 73 | BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. 74 | uint16_t m_conn_id; 75 | // int m_deviceType; 76 | esp_gatt_if_t m_gattc_if; 77 | bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. 78 | bool m_isConnected = false; // Are we currently connected. 79 | 80 | BLEClientCallbacks* m_pClientCallbacks; 81 | FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); 82 | FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); 83 | FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); 84 | FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); 85 | std::map m_servicesMap; 86 | std::map m_servicesMapByInstID; 87 | void clearServices(); // Clear any existing services. 88 | uint16_t m_mtu = 23; 89 | }; // class BLEDevice 90 | 91 | 92 | /** 93 | * @brief Callbacks associated with a %BLE client. 94 | */ 95 | class BLEClientCallbacks { 96 | public: 97 | virtual ~BLEClientCallbacks() {}; 98 | virtual void onConnect(BLEClient *pClient) = 0; 99 | virtual void onDisconnect(BLEClient *pClient) = 0; 100 | }; 101 | 102 | #endif // CONFIG_BT_ENABLED 103 | #endif /* MAIN_BLEDEVICE_H_ */ 104 | -------------------------------------------------------------------------------- /src/BLEDescriptor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEDescriptor.cpp 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "sdkconfig.h" 14 | #include 15 | #include "BLEService.h" 16 | #include "BLEDescriptor.h" 17 | #include "GeneralUtils.h" 18 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 19 | #include "esp32-hal-log.h" 20 | #define LOG_TAG "" 21 | #else 22 | #include "esp_log.h" 23 | static const char* LOG_TAG = "BLEDescriptor"; 24 | #endif 25 | 26 | 27 | 28 | 29 | #define NULL_HANDLE (0xffff) 30 | 31 | 32 | /** 33 | * @brief BLEDescriptor constructor. 34 | */ 35 | BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) { 36 | } 37 | 38 | /** 39 | * @brief BLEDescriptor constructor. 40 | */ 41 | BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) { 42 | m_bleUUID = uuid; 43 | m_value.attr_len = 0; // Initial length is 0. 44 | m_value.attr_max_len = max_len; // Maximum length of the data. 45 | m_handle = NULL_HANDLE; // Handle is initially unknown. 46 | m_pCharacteristic = nullptr; // No initial characteristic. 47 | m_pCallback = nullptr; // No initial callback. 48 | 49 | m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. 50 | } // BLEDescriptor 51 | 52 | 53 | /** 54 | * @brief BLEDescriptor destructor. 55 | */ 56 | BLEDescriptor::~BLEDescriptor() { 57 | free(m_value.attr_value); // Release the storage we created in the constructor. 58 | } // ~BLEDescriptor 59 | 60 | 61 | /** 62 | * @brief Execute the creation of the descriptor with the BLE runtime in ESP. 63 | * @param [in] pCharacteristic The characteristic to which to register this descriptor. 64 | */ 65 | void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { 66 | ESP_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str()); 67 | 68 | if (m_handle != NULL_HANDLE) { 69 | ESP_LOGE(LOG_TAG, "Descriptor already has a handle."); 70 | return; 71 | } 72 | 73 | m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. 74 | 75 | esp_attr_control_t control; 76 | control.auto_rsp = ESP_GATT_AUTO_RSP; 77 | m_semaphoreCreateEvt.take("executeCreate"); 78 | esp_err_t errRc = ::esp_ble_gatts_add_char_descr( 79 | pCharacteristic->getService()->getHandle(), 80 | getUUID().getNative(), 81 | (esp_gatt_perm_t)m_permissions, 82 | &m_value, 83 | &control); 84 | if (errRc != ESP_OK) { 85 | ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 86 | return; 87 | } 88 | 89 | m_semaphoreCreateEvt.wait("executeCreate"); 90 | ESP_LOGD(LOG_TAG, "<< executeCreate"); 91 | } // executeCreate 92 | 93 | 94 | /** 95 | * @brief Get the BLE handle for this descriptor. 96 | * @return The handle for this descriptor. 97 | */ 98 | uint16_t BLEDescriptor::getHandle() { 99 | return m_handle; 100 | } // getHandle 101 | 102 | 103 | /** 104 | * @brief Get the length of the value of this descriptor. 105 | * @return The length (in bytes) of the value of this descriptor. 106 | */ 107 | size_t BLEDescriptor::getLength() { 108 | return m_value.attr_len; 109 | } // getLength 110 | 111 | 112 | /** 113 | * @brief Get the UUID of the descriptor. 114 | */ 115 | BLEUUID BLEDescriptor::getUUID() { 116 | return m_bleUUID; 117 | } // getUUID 118 | 119 | 120 | 121 | /** 122 | * @brief Get the value of this descriptor. 123 | * @return A pointer to the value of this descriptor. 124 | */ 125 | uint8_t* BLEDescriptor::getValue() { 126 | return m_value.attr_value; 127 | } // getValue 128 | 129 | 130 | /** 131 | * @brief Handle GATT server events for the descripttor. 132 | * @param [in] event 133 | * @param [in] gatts_if 134 | * @param [in] param 135 | */ 136 | void BLEDescriptor::handleGATTServerEvent( 137 | esp_gatts_cb_event_t event, 138 | esp_gatt_if_t gatts_if, 139 | esp_ble_gatts_cb_param_t* param) { 140 | switch (event) { 141 | // ESP_GATTS_ADD_CHAR_DESCR_EVT 142 | // 143 | // add_char_descr: 144 | // - esp_gatt_status_t status 145 | // - uint16_t attr_handle 146 | // - uint16_t service_handle 147 | // - esp_bt_uuid_t char_uuid 148 | case ESP_GATTS_ADD_CHAR_DESCR_EVT: { 149 | if (m_pCharacteristic != nullptr && 150 | m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) && 151 | m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && 152 | m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { 153 | setHandle(param->add_char_descr.attr_handle); 154 | m_semaphoreCreateEvt.give(); 155 | } 156 | break; 157 | } // ESP_GATTS_ADD_CHAR_DESCR_EVT 158 | 159 | // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived. 160 | // 161 | // write: 162 | // - uint16_t conn_id 163 | // - uint16_t trans_id 164 | // - esp_bd_addr_t bda 165 | // - uint16_t handle 166 | // - uint16_t offset 167 | // - bool need_rsp 168 | // - bool is_prep 169 | // - uint16_t len 170 | // - uint8_t *value 171 | case ESP_GATTS_WRITE_EVT: { 172 | if (param->write.handle == m_handle) { 173 | setValue(param->write.value, param->write.len); // Set the value of the descriptor. 174 | 175 | if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. 176 | m_pCallback->onWrite(this); // Invoke the onWrite callback handler. 177 | } 178 | } // End of ... this is our handle. 179 | 180 | break; 181 | } // ESP_GATTS_WRITE_EVT 182 | 183 | // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived. 184 | // 185 | // read: 186 | // - uint16_t conn_id 187 | // - uint32_t trans_id 188 | // - esp_bd_addr_t bda 189 | // - uint16_t handle 190 | // - uint16_t offset 191 | // - bool is_long 192 | // - bool need_rsp 193 | // 194 | case ESP_GATTS_READ_EVT: { 195 | if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it 196 | 197 | if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now. 198 | m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. 199 | } 200 | 201 | } // End of this is our handle 202 | break; 203 | } // ESP_GATTS_READ_EVT 204 | 205 | default: 206 | break; 207 | } // switch event 208 | } // handleGATTServerEvent 209 | 210 | 211 | /** 212 | * @brief Set the callback handlers for this descriptor. 213 | * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. 214 | */ 215 | void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) { 216 | ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback); 217 | m_pCallback = pCallback; 218 | ESP_LOGD(LOG_TAG, "<< setCallbacks"); 219 | } // setCallbacks 220 | 221 | 222 | /** 223 | * @brief Set the handle of this descriptor. 224 | * Set the handle of this descriptor to be the supplied value. 225 | * @param [in] handle The handle to be associated with this descriptor. 226 | * @return N/A. 227 | */ 228 | void BLEDescriptor::setHandle(uint16_t handle) { 229 | ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); 230 | m_handle = handle; 231 | ESP_LOGD(LOG_TAG, "<< setHandle()"); 232 | } // setHandle 233 | 234 | 235 | /** 236 | * @brief Set the value of the descriptor. 237 | * @param [in] data The data to set for the descriptor. 238 | * @param [in] length The length of the data in bytes. 239 | */ 240 | void BLEDescriptor::setValue(uint8_t* data, size_t length) { 241 | if (length > ESP_GATT_MAX_ATTR_LEN) { 242 | ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); 243 | return; 244 | } 245 | m_value.attr_len = length; 246 | memcpy(m_value.attr_value, data, length); 247 | } // setValue 248 | 249 | 250 | /** 251 | * @brief Set the value of the descriptor. 252 | * @param [in] value The value of the descriptor in string form. 253 | */ 254 | void BLEDescriptor::setValue(std::string value) { 255 | setValue((uint8_t*) value.data(), value.length()); 256 | } // setValue 257 | 258 | void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) { 259 | m_permissions = perm; 260 | } 261 | 262 | /** 263 | * @brief Return a string representation of the descriptor. 264 | * @return A string representation of the descriptor. 265 | */ 266 | std::string BLEDescriptor::toString() { 267 | std::stringstream stringstream; 268 | stringstream << std::hex << std::setfill('0'); 269 | stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle; 270 | return stringstream.str(); 271 | } // toString 272 | 273 | 274 | BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} 275 | 276 | /** 277 | * @brief Callback function to support a read request. 278 | * @param [in] pDescriptor The descriptor that is the source of the event. 279 | */ 280 | void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) { 281 | ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default"); 282 | ESP_LOGD("BLEDescriptorCallbacks", "<< onRead"); 283 | } // onRead 284 | 285 | 286 | /** 287 | * @brief Callback function to support a write request. 288 | * @param [in] pDescriptor The descriptor that is the source of the event. 289 | */ 290 | void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) { 291 | ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default"); 292 | ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite"); 293 | } // onWrite 294 | 295 | 296 | #endif /* CONFIG_BT_ENABLED */ 297 | -------------------------------------------------------------------------------- /src/BLEDescriptor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEDescriptor.h 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | #include "BLEUUID.h" 14 | #include "BLECharacteristic.h" 15 | #include 16 | #include "FreeRTOS.h" 17 | 18 | class BLEService; 19 | class BLECharacteristic; 20 | class BLEDescriptorCallbacks; 21 | 22 | /** 23 | * @brief A model of a %BLE descriptor. 24 | */ 25 | class BLEDescriptor { 26 | public: 27 | BLEDescriptor(const char* uuid, uint16_t max_len = 100); 28 | BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); 29 | virtual ~BLEDescriptor(); 30 | 31 | uint16_t getHandle(); // Get the handle of the descriptor. 32 | size_t getLength(); // Get the length of the value of the descriptor. 33 | BLEUUID getUUID(); // Get the UUID of the descriptor. 34 | uint8_t* getValue(); // Get a pointer to the value of the descriptor. 35 | void handleGATTServerEvent( 36 | esp_gatts_cb_event_t event, 37 | esp_gatt_if_t gatts_if, 38 | esp_ble_gatts_cb_param_t* param); 39 | 40 | void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor. 41 | void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. 42 | void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. 43 | void setValue(std::string value); // Set the value of the descriptor as a data buffer. 44 | 45 | std::string toString(); // Convert the descriptor to a string representation. 46 | 47 | private: 48 | friend class BLEDescriptorMap; 49 | friend class BLECharacteristic; 50 | BLEUUID m_bleUUID; 51 | uint16_t m_handle; 52 | BLEDescriptorCallbacks* m_pCallback; 53 | BLECharacteristic* m_pCharacteristic; 54 | esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; 55 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 56 | esp_attr_value_t m_value; 57 | 58 | void executeCreate(BLECharacteristic* pCharacteristic); 59 | void setHandle(uint16_t handle); 60 | }; // BLEDescriptor 61 | 62 | 63 | /** 64 | * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. 65 | * 66 | * When a server application creates a %BLE descriptor, we may wish to be informed when there is either 67 | * a read or write request to the descriptors value. An application can register a 68 | * sub-classed instance of this class and will be notified when such an event happens. 69 | */ 70 | class BLEDescriptorCallbacks { 71 | public: 72 | virtual ~BLEDescriptorCallbacks(); 73 | virtual void onRead(BLEDescriptor* pDescriptor); 74 | virtual void onWrite(BLEDescriptor* pDescriptor); 75 | }; 76 | #endif /* CONFIG_BT_ENABLED */ 77 | #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ 78 | -------------------------------------------------------------------------------- /src/BLEDescriptorMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEDescriptorMap.cpp 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include "BLECharacteristic.h" 12 | #include "BLEDescriptor.h" 13 | #include // ESP32 BLE 14 | #ifdef ARDUINO_ARCH_ESP32 15 | #include "esp32-hal-log.h" 16 | #endif 17 | 18 | /** 19 | * @brief Return the descriptor by UUID. 20 | * @param [in] UUID The UUID to look up the descriptor. 21 | * @return The descriptor. If not present, then nullptr is returned. 22 | */ 23 | BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { 24 | return getByUUID(BLEUUID(uuid)); 25 | } 26 | 27 | 28 | /** 29 | * @brief Return the descriptor by UUID. 30 | * @param [in] UUID The UUID to look up the descriptor. 31 | * @return The descriptor. If not present, then nullptr is returned. 32 | */ 33 | BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) { 34 | for (auto &myPair : m_uuidMap) { 35 | if (myPair.first->getUUID().equals(uuid)) { 36 | return myPair.first; 37 | } 38 | } 39 | //return m_uuidMap.at(uuid.toString()); 40 | return nullptr; 41 | } // getByUUID 42 | 43 | 44 | /** 45 | * @brief Return the descriptor by handle. 46 | * @param [in] handle The handle to look up the descriptor. 47 | * @return The descriptor. 48 | */ 49 | BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) { 50 | return m_handleMap.at(handle); 51 | } // getByHandle 52 | 53 | 54 | /** 55 | * @brief Set the descriptor by UUID. 56 | * @param [in] uuid The uuid of the descriptor. 57 | * @param [in] characteristic The descriptor to cache. 58 | * @return N/A. 59 | */ 60 | void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ 61 | m_uuidMap.insert(std::pair(pDescriptor, uuid)); 62 | } // setByUUID 63 | 64 | 65 | 66 | /** 67 | * @brief Set the descriptor by UUID. 68 | * @param [in] uuid The uuid of the descriptor. 69 | * @param [in] characteristic The descriptor to cache. 70 | * @return N/A. 71 | */ 72 | void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) { 73 | m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); 74 | } // setByUUID 75 | 76 | 77 | /** 78 | * @brief Set the descriptor by handle. 79 | * @param [in] handle The handle of the descriptor. 80 | * @param [in] descriptor The descriptor to cache. 81 | * @return N/A. 82 | */ 83 | void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) { 84 | m_handleMap.insert(std::pair(handle, pDescriptor)); 85 | } // setByHandle 86 | 87 | 88 | /** 89 | * @brief Return a string representation of the descriptor map. 90 | * @return A string representation of the descriptor map. 91 | */ 92 | std::string BLEDescriptorMap::toString() { 93 | std::stringstream stringStream; 94 | stringStream << std::hex << std::setfill('0'); 95 | int count = 0; 96 | for (auto &myPair : m_uuidMap) { 97 | if (count > 0) { 98 | stringStream << "\n"; 99 | } 100 | count++; 101 | stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); 102 | } 103 | return stringStream.str(); 104 | } // toString 105 | 106 | 107 | /** 108 | * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping 109 | * @param [in] event 110 | * @param [in] gatts_if 111 | * @param [in] param 112 | */ 113 | void BLEDescriptorMap::handleGATTServerEvent( 114 | esp_gatts_cb_event_t event, 115 | esp_gatt_if_t gatts_if, 116 | esp_ble_gatts_cb_param_t* param) { 117 | // Invoke the handler for every descriptor we have. 118 | for (auto &myPair : m_uuidMap) { 119 | myPair.first->handleGATTServerEvent(event, gatts_if, param); 120 | } 121 | } // handleGATTServerEvent 122 | 123 | 124 | /** 125 | * @brief Get the first descriptor in the map. 126 | * @return The first descriptor in the map. 127 | */ 128 | BLEDescriptor* BLEDescriptorMap::getFirst() { 129 | m_iterator = m_uuidMap.begin(); 130 | if (m_iterator == m_uuidMap.end()) return nullptr; 131 | BLEDescriptor* pRet = m_iterator->first; 132 | m_iterator++; 133 | return pRet; 134 | } // getFirst 135 | 136 | 137 | /** 138 | * @brief Get the next descriptor in the map. 139 | * @return The next descriptor in the map. 140 | */ 141 | BLEDescriptor* BLEDescriptorMap::getNext() { 142 | if (m_iterator == m_uuidMap.end()) return nullptr; 143 | BLEDescriptor* pRet = m_iterator->first; 144 | m_iterator++; 145 | return pRet; 146 | } // getNext 147 | #endif /* CONFIG_BT_ENABLED */ 148 | -------------------------------------------------------------------------------- /src/BLEDevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEDevice.h 3 | * 4 | * Created on: Mar 16, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef MAIN_BLEDevice_H_ 9 | #define MAIN_BLEDevice_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include // ESP32 BLE 13 | #include // ESP32 BLE 14 | #include // Part of C++ STL 15 | #include 16 | #include 17 | 18 | #include "BLEServer.h" 19 | #include "BLEClient.h" 20 | #include "BLEUtils.h" 21 | #include "BLEScan.h" 22 | #include "BLEAddress.h" 23 | 24 | /** 25 | * @brief BLE functions. 26 | */ 27 | typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); 28 | typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); 29 | typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param); 30 | 31 | class BLEDevice { 32 | public: 33 | 34 | static BLEClient* createClient(); // Create a new BLE client. 35 | static BLEServer* createServer(); // Cretae a new BLE server. 36 | static BLEAddress getAddress(); // Retrieve our own local BD address. 37 | static BLEScan* getScan(); // Get the scan object 38 | static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server. 39 | static void init(std::string deviceName); // Initialize the local BLE environment. 40 | static void setPower(esp_power_level_t powerLevel); // Set our power level. 41 | static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server. 42 | static std::string toString(); // Return a string representation of our device. 43 | static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list. 44 | static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list. 45 | static void setEncryptionLevel(esp_ble_sec_act_t level); 46 | static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); 47 | static esp_err_t setMTU(uint16_t mtu); 48 | static uint16_t getMTU(); 49 | static bool getInitialized(); // Returns the state of the device, is it initialized or not? 50 | /* move advertising to BLEDevice for saving ram and flash in beacons */ 51 | static BLEAdvertising* getAdvertising(); 52 | static void startAdvertising(); 53 | static uint16_t m_appId; 54 | /* multi connect */ 55 | static std::map getPeerDevices(bool client); 56 | static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); 57 | static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id); 58 | static void removePeerDevice(uint16_t conn_id, bool client); 59 | static BLEClient* getClientByGattIf(uint16_t conn_id); 60 | static void setCustomGapHandler(gap_event_handler handler); 61 | static void setCustomGattcHandler(gattc_event_handler handler); 62 | static void setCustomGattsHandler(gatts_event_handler handler); 63 | static void deinit(bool release_memory = false); 64 | static uint16_t m_localMTU; 65 | static esp_ble_sec_act_t m_securityLevel; 66 | 67 | private: 68 | static BLEServer* m_pServer; 69 | static BLEScan* m_pScan; 70 | static BLEClient* m_pClient; 71 | static BLESecurityCallbacks* m_securityCallbacks; 72 | static BLEAdvertising* m_bleAdvertising; 73 | static esp_gatt_if_t getGattcIF(); 74 | static std::map m_connectedClientsMap; 75 | 76 | static void gattClientEventHandler( 77 | esp_gattc_cb_event_t event, 78 | esp_gatt_if_t gattc_if, 79 | esp_ble_gattc_cb_param_t* param); 80 | 81 | static void gattServerEventHandler( 82 | esp_gatts_cb_event_t event, 83 | esp_gatt_if_t gatts_if, 84 | esp_ble_gatts_cb_param_t* param); 85 | 86 | static void gapEventHandler( 87 | esp_gap_ble_cb_event_t event, 88 | esp_ble_gap_cb_param_t* param); 89 | 90 | public: 91 | /* custom gap and gatt handlers for flexibility */ 92 | static gap_event_handler m_customGapHandler; 93 | static gattc_event_handler m_customGattcHandler; 94 | static gatts_event_handler m_customGattsHandler; 95 | 96 | }; // class BLE 97 | 98 | #endif // CONFIG_BT_ENABLED 99 | #endif /* MAIN_BLEDevice_H_ */ 100 | -------------------------------------------------------------------------------- /src/BLEEddystoneTLM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEEddystoneTLM.cpp 3 | * 4 | * Created on: Mar 12, 2018 5 | * Author: pcbreflux 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include 12 | #include "BLEEddystoneTLM.h" 13 | 14 | static const char LOG_TAG[] = "BLEEddystoneTLM"; 15 | #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) 16 | #define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) 17 | 18 | BLEEddystoneTLM::BLEEddystoneTLM() { 19 | beaconUUID = 0xFEAA; 20 | m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; 21 | m_eddystoneData.version = 0; 22 | m_eddystoneData.volt = 3300; // 3300mV = 3.3V 23 | m_eddystoneData.temp = (uint16_t) ((float) 23.00); 24 | m_eddystoneData.advCount = 0; 25 | m_eddystoneData.tmil = 0; 26 | } // BLEEddystoneTLM 27 | 28 | std::string BLEEddystoneTLM::getData() { 29 | return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); 30 | } // getData 31 | 32 | BLEUUID BLEEddystoneTLM::getUUID() { 33 | return BLEUUID(beaconUUID); 34 | } // getUUID 35 | 36 | uint8_t BLEEddystoneTLM::getVersion() { 37 | return m_eddystoneData.version; 38 | } // getVersion 39 | 40 | uint16_t BLEEddystoneTLM::getVolt() { 41 | return m_eddystoneData.volt; 42 | } // getVolt 43 | 44 | float BLEEddystoneTLM::getTemp() { 45 | return (float)m_eddystoneData.temp; 46 | } // getTemp 47 | 48 | uint32_t BLEEddystoneTLM::getCount() { 49 | return m_eddystoneData.advCount; 50 | } // getCount 51 | 52 | uint32_t BLEEddystoneTLM::getTime() { 53 | return m_eddystoneData.tmil; 54 | } // getTime 55 | 56 | std::string BLEEddystoneTLM::toString() { 57 | std::stringstream ss; 58 | std::string out = ""; 59 | uint32_t rawsec; 60 | ss << "Version "; 61 | ss << std::dec << m_eddystoneData.version; 62 | ss << "\n"; 63 | 64 | ss << "Battery Voltage "; 65 | ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt); 66 | ss << " mV\n"; 67 | 68 | ss << "Temperature "; 69 | ss << (float) m_eddystoneData.temp; 70 | ss << " °C\n"; 71 | 72 | ss << "Adv. Count "; 73 | ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount); 74 | 75 | ss << "\n"; 76 | 77 | ss << "Time "; 78 | 79 | rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); 80 | std::stringstream buffstream; 81 | buffstream << "0000"; 82 | buffstream << std::dec << rawsec / 864000; 83 | std::string buff = buffstream.str(); 84 | 85 | ss << buff.substr(buff.length() - 4, buff.length()); 86 | ss << "."; 87 | 88 | buffstream.str(""); 89 | buffstream.clear(); 90 | buffstream << "00"; 91 | buffstream << std::dec << (rawsec / 36000) % 24; 92 | buff = buffstream.str(); 93 | ss << buff.substr(buff.length()-2, buff.length()); 94 | ss << ":"; 95 | 96 | buffstream.str(""); 97 | buffstream.clear(); 98 | buffstream << "00"; 99 | buffstream << std::dec << (rawsec / 600) % 60; 100 | buff = buffstream.str(); 101 | ss << buff.substr(buff.length() - 2, buff.length()); 102 | ss << ":"; 103 | 104 | buffstream.str(""); 105 | buffstream.clear(); 106 | buffstream << "00"; 107 | buffstream << std::dec << (rawsec / 10) % 60; 108 | buff = buffstream.str(); 109 | ss << buff.substr(buff.length() - 2, buff.length()); 110 | ss << "\n"; 111 | 112 | return ss.str(); 113 | } // toString 114 | 115 | /** 116 | * Set the raw data for the beacon record. 117 | */ 118 | void BLEEddystoneTLM::setData(std::string data) { 119 | if (data.length() != sizeof(m_eddystoneData)) { 120 | ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData)); 121 | return; 122 | } 123 | memcpy(&m_eddystoneData, data.data(), data.length()); 124 | } // setData 125 | 126 | void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) { 127 | beaconUUID = l_uuid.getNative()->uuid.uuid16; 128 | } // setUUID 129 | 130 | void BLEEddystoneTLM::setVersion(uint8_t version) { 131 | m_eddystoneData.version = version; 132 | } // setVersion 133 | 134 | void BLEEddystoneTLM::setVolt(uint16_t volt) { 135 | m_eddystoneData.volt = volt; 136 | } // setVolt 137 | 138 | void BLEEddystoneTLM::setTemp(float temp) { 139 | m_eddystoneData.temp = (uint16_t)temp; 140 | } // setTemp 141 | 142 | void BLEEddystoneTLM::setCount(uint32_t advCount) { 143 | m_eddystoneData.advCount = advCount; 144 | } // setCount 145 | 146 | void BLEEddystoneTLM::setTime(uint32_t tmil) { 147 | m_eddystoneData.tmil = tmil; 148 | } // setTime 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/BLEEddystoneTLM.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEEddystoneTLM.cpp 3 | * 4 | * Created on: Mar 12, 2018 5 | * Author: pcbreflux 6 | */ 7 | 8 | #ifndef _BLEEddystoneTLM_H_ 9 | #define _BLEEddystoneTLM_H_ 10 | #include "BLEUUID.h" 11 | 12 | #define EDDYSTONE_TLM_FRAME_TYPE 0x20 13 | 14 | /** 15 | * @brief Representation of a beacon. 16 | * See: 17 | * * https://github.com/google/eddystone 18 | */ 19 | class BLEEddystoneTLM { 20 | public: 21 | BLEEddystoneTLM(); 22 | std::string getData(); 23 | BLEUUID getUUID(); 24 | uint8_t getVersion(); 25 | uint16_t getVolt(); 26 | float getTemp(); 27 | uint32_t getCount(); 28 | uint32_t getTime(); 29 | std::string toString(); 30 | void setData(std::string data); 31 | void setUUID(BLEUUID l_uuid); 32 | void setVersion(uint8_t version); 33 | void setVolt(uint16_t volt); 34 | void setTemp(float temp); 35 | void setCount(uint32_t advCount); 36 | void setTime(uint32_t tmil); 37 | 38 | private: 39 | uint16_t beaconUUID; 40 | struct { 41 | uint8_t frameType; 42 | uint8_t version; 43 | uint16_t volt; 44 | uint16_t temp; 45 | uint32_t advCount; 46 | uint32_t tmil; 47 | } __attribute__((packed)) m_eddystoneData; 48 | 49 | }; // BLEEddystoneTLM 50 | 51 | #endif /* _BLEEddystoneTLM_H_ */ 52 | -------------------------------------------------------------------------------- /src/BLEEddystoneURL.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEEddystoneURL.cpp 3 | * 4 | * Created on: Mar 12, 2018 5 | * Author: pcbreflux 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include "BLEEddystoneURL.h" 12 | 13 | static const char LOG_TAG[] = "BLEEddystoneURL"; 14 | 15 | BLEEddystoneURL::BLEEddystoneURL() { 16 | beaconUUID = 0xFEAA; 17 | lengthURL = 0; 18 | m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; 19 | m_eddystoneData.advertisedTxPower = 0; 20 | memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); 21 | } // BLEEddystoneURL 22 | 23 | std::string BLEEddystoneURL::getData() { 24 | return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); 25 | } // getData 26 | 27 | BLEUUID BLEEddystoneURL::getUUID() { 28 | return BLEUUID(beaconUUID); 29 | } // getUUID 30 | 31 | int8_t BLEEddystoneURL::getPower() { 32 | return m_eddystoneData.advertisedTxPower; 33 | } // getPower 34 | 35 | std::string BLEEddystoneURL::getURL() { 36 | return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); 37 | } // getURL 38 | 39 | std::string BLEEddystoneURL::getDecodedURL() { 40 | std::string decodedURL = ""; 41 | 42 | switch (m_eddystoneData.url[0]) { 43 | case 0x00: 44 | decodedURL += "http://www."; 45 | break; 46 | case 0x01: 47 | decodedURL += "https://www."; 48 | break; 49 | case 0x02: 50 | decodedURL += "http://"; 51 | break; 52 | case 0x03: 53 | decodedURL += "https://"; 54 | break; 55 | default: 56 | decodedURL += m_eddystoneData.url[0]; 57 | } 58 | 59 | for (int i = 1; i < lengthURL; i++) { 60 | if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { 61 | decodedURL += m_eddystoneData.url[i]; 62 | } else { 63 | switch (m_eddystoneData.url[i]) { 64 | case 0x00: 65 | decodedURL += ".com/"; 66 | break; 67 | case 0x01: 68 | decodedURL += ".org/"; 69 | break; 70 | case 0x02: 71 | decodedURL += ".edu/"; 72 | break; 73 | case 0x03: 74 | decodedURL += ".net/"; 75 | break; 76 | case 0x04: 77 | decodedURL += ".info/"; 78 | break; 79 | case 0x05: 80 | decodedURL += ".biz/"; 81 | break; 82 | case 0x06: 83 | decodedURL += ".gov/"; 84 | break; 85 | case 0x07: 86 | decodedURL += ".com"; 87 | break; 88 | case 0x08: 89 | decodedURL += ".org"; 90 | break; 91 | case 0x09: 92 | decodedURL += ".edu"; 93 | break; 94 | case 0x0A: 95 | decodedURL += ".net"; 96 | break; 97 | case 0x0B: 98 | decodedURL += ".info"; 99 | break; 100 | case 0x0C: 101 | decodedURL += ".biz"; 102 | break; 103 | case 0x0D: 104 | decodedURL += ".gov"; 105 | break; 106 | default: 107 | break; 108 | } 109 | } 110 | } 111 | return decodedURL; 112 | } // getDecodedURL 113 | 114 | 115 | 116 | /** 117 | * Set the raw data for the beacon record. 118 | */ 119 | void BLEEddystoneURL::setData(std::string data) { 120 | if (data.length() > sizeof(m_eddystoneData)) { 121 | ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData)); 122 | return; 123 | } 124 | memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); 125 | memcpy(&m_eddystoneData, data.data(), data.length()); 126 | lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); 127 | } // setData 128 | 129 | void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { 130 | beaconUUID = l_uuid.getNative()->uuid.uuid16; 131 | } // setUUID 132 | 133 | void BLEEddystoneURL::setPower(int8_t advertisedTxPower) { 134 | m_eddystoneData.advertisedTxPower = advertisedTxPower; 135 | } // setPower 136 | 137 | void BLEEddystoneURL::setURL(std::string url) { 138 | if (url.length() > sizeof(m_eddystoneData.url)) { 139 | ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url)); 140 | return; 141 | } 142 | memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); 143 | memcpy(m_eddystoneData.url, url.data(), url.length()); 144 | lengthURL = url.length(); 145 | } // setURL 146 | 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /src/BLEEddystoneURL.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEEddystoneURL.cpp 3 | * 4 | * Created on: Mar 12, 2018 5 | * Author: pcbreflux 6 | */ 7 | 8 | #ifndef _BLEEddystoneURL_H_ 9 | #define _BLEEddystoneURL_H_ 10 | #include "BLEUUID.h" 11 | 12 | #define EDDYSTONE_URL_FRAME_TYPE 0x10 13 | 14 | /** 15 | * @brief Representation of a beacon. 16 | * See: 17 | * * https://github.com/google/eddystone 18 | */ 19 | class BLEEddystoneURL { 20 | public: 21 | BLEEddystoneURL(); 22 | std::string getData(); 23 | BLEUUID getUUID(); 24 | int8_t getPower(); 25 | std::string getURL(); 26 | std::string getDecodedURL(); 27 | void setData(std::string data); 28 | void setUUID(BLEUUID l_uuid); 29 | void setPower(int8_t advertisedTxPower); 30 | void setURL(std::string url); 31 | 32 | private: 33 | uint16_t beaconUUID; 34 | uint8_t lengthURL; 35 | struct { 36 | uint8_t frameType; 37 | int8_t advertisedTxPower; 38 | uint8_t url[16]; 39 | } __attribute__((packed)) m_eddystoneData; 40 | 41 | }; // BLEEddystoneURL 42 | 43 | #endif /* _BLEEddystoneURL_H_ */ 44 | -------------------------------------------------------------------------------- /src/BLEExceptions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLExceptions.cpp 3 | * 4 | * Created on: Nov 27, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #include "BLEExceptions.h" 9 | 10 | -------------------------------------------------------------------------------- /src/BLEExceptions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLExceptions.h 3 | * 4 | * Created on: Nov 27, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ 10 | #include "sdkconfig.h" 11 | 12 | #if CONFIG_CXX_EXCEPTIONS != 1 13 | #error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." 14 | #endif 15 | 16 | #include 17 | 18 | 19 | class BLEDisconnectedException : public std::exception { 20 | const char* what() const throw () { 21 | return "BLE Disconnected"; 22 | } 23 | }; 24 | 25 | class BLEUuidNotFoundException : public std::exception { 26 | const char* what() const throw () { 27 | return "No such UUID"; 28 | } 29 | }; 30 | 31 | #endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ 32 | -------------------------------------------------------------------------------- /src/BLEHIDDevice.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEHIDDevice.cpp 3 | * 4 | * Created on: Jan 03, 2018 5 | * Author: chegewara 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | 10 | #include "BLEHIDDevice.h" 11 | #include "BLE2904.h" 12 | 13 | 14 | BLEHIDDevice::BLEHIDDevice(BLEServer* server) { 15 | /* 16 | * Here we create mandatory services described in bluetooth specification 17 | */ 18 | m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a)); 19 | m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40); 20 | m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); 21 | 22 | /* 23 | * Mandatory characteristic for device info service 24 | */ 25 | m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ); 26 | 27 | /* 28 | * Mandatory characteristics for HID service 29 | */ 30 | m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ); 31 | m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ); 32 | m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); 33 | m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); 34 | 35 | /* 36 | * Mandatory battery level characteristic with notification and presence descriptor 37 | */ 38 | BLE2904* batteryLevelDescriptor = new BLE2904(); 39 | batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8); 40 | batteryLevelDescriptor->setNamespace(1); 41 | batteryLevelDescriptor->setUnit(0x27ad); 42 | 43 | m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); 44 | m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); 45 | m_batteryLevelCharacteristic->addDescriptor(new BLE2902()); 46 | 47 | /* 48 | * This value is setup here because its default value in most usage cases, its very rare to use boot mode 49 | * and we want to simplify library using as much as possible 50 | */ 51 | const uint8_t pMode[] = { 0x01 }; 52 | protocolMode()->setValue((uint8_t*) pMode, 1); 53 | } 54 | 55 | BLEHIDDevice::~BLEHIDDevice() { 56 | } 57 | 58 | /* 59 | * @brief 60 | */ 61 | void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { 62 | m_reportMapCharacteristic->setValue(map, size); 63 | } 64 | 65 | /* 66 | * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service 67 | */ 68 | void BLEHIDDevice::startServices() { 69 | m_deviceInfoService->start(); 70 | m_hidService->start(); 71 | m_batteryService->start(); 72 | } 73 | 74 | /* 75 | * @brief Create manufacturer characteristic (this characteristic is optional) 76 | */ 77 | BLECharacteristic* BLEHIDDevice::manufacturer() { 78 | m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ); 79 | return m_manufacturerCharacteristic; 80 | } 81 | 82 | /* 83 | * @brief Set manufacturer name 84 | * @param [in] name manufacturer name 85 | */ 86 | void BLEHIDDevice::manufacturer(std::string name) { 87 | m_manufacturerCharacteristic->setValue(name); 88 | } 89 | 90 | /* 91 | * @brief 92 | */ 93 | void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { 94 | uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; 95 | m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); 96 | } 97 | 98 | /* 99 | * @brief 100 | */ 101 | void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { 102 | uint8_t info[] = { 0x11, 0x1, country, flags }; 103 | m_hidInfoCharacteristic->setValue(info, sizeof(info)); 104 | } 105 | 106 | /* 107 | * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used 108 | * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic 109 | * @return pointer to new input report characteristic 110 | */ 111 | BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { 112 | BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); 113 | BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); 114 | BLE2902* p2902 = new BLE2902(); 115 | inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 116 | inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 117 | p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 118 | 119 | uint8_t desc1_val[] = { reportID, 0x01 }; 120 | inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); 121 | inputReportCharacteristic->addDescriptor(p2902); 122 | inputReportCharacteristic->addDescriptor(inputReportDescriptor); 123 | 124 | return inputReportCharacteristic; 125 | } 126 | 127 | /* 128 | * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used 129 | * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic 130 | * @return Pointer to new output report characteristic 131 | */ 132 | BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { 133 | BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); 134 | BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); 135 | outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 136 | outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 137 | 138 | uint8_t desc1_val[] = { reportID, 0x02 }; 139 | outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); 140 | outputReportCharacteristic->addDescriptor(outputReportDescriptor); 141 | 142 | return outputReportCharacteristic; 143 | } 144 | 145 | /* 146 | * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used 147 | * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic 148 | * @return Pointer to new feature report characteristic 149 | */ 150 | BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { 151 | BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); 152 | BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); 153 | 154 | featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 155 | featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 156 | 157 | uint8_t desc1_val[] = { reportID, 0x03 }; 158 | featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); 159 | featureReportCharacteristic->addDescriptor(featureReportDescriptor); 160 | 161 | return featureReportCharacteristic; 162 | } 163 | 164 | /* 165 | * @brief 166 | */ 167 | BLECharacteristic* BLEHIDDevice::bootInput() { 168 | BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY); 169 | bootInputCharacteristic->addDescriptor(new BLE2902()); 170 | 171 | return bootInputCharacteristic; 172 | } 173 | 174 | /* 175 | * @brief 176 | */ 177 | BLECharacteristic* BLEHIDDevice::bootOutput() { 178 | return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); 179 | } 180 | 181 | /* 182 | * @brief 183 | */ 184 | BLECharacteristic* BLEHIDDevice::hidControl() { 185 | return m_hidControlCharacteristic; 186 | } 187 | 188 | /* 189 | * @brief 190 | */ 191 | BLECharacteristic* BLEHIDDevice::protocolMode() { 192 | return m_protocolModeCharacteristic; 193 | } 194 | 195 | void BLEHIDDevice::setBatteryLevel(uint8_t level) { 196 | m_batteryLevelCharacteristic->setValue(&level, 1); 197 | } 198 | /* 199 | * @brief Returns battery level characteristic 200 | * @ return battery level characteristic 201 | *//* 202 | BLECharacteristic* BLEHIDDevice::batteryLevel() { 203 | return m_batteryLevelCharacteristic; 204 | } 205 | 206 | 207 | 208 | BLECharacteristic* BLEHIDDevice::reportMap() { 209 | return m_reportMapCharacteristic; 210 | } 211 | 212 | BLECharacteristic* BLEHIDDevice::pnp() { 213 | return m_pnpCharacteristic; 214 | } 215 | 216 | 217 | BLECharacteristic* BLEHIDDevice::hidInfo() { 218 | return m_hidInfoCharacteristic; 219 | } 220 | */ 221 | /* 222 | * @brief 223 | */ 224 | BLEService* BLEHIDDevice::deviceInfo() { 225 | return m_deviceInfoService; 226 | } 227 | 228 | /* 229 | * @brief 230 | */ 231 | BLEService* BLEHIDDevice::hidService() { 232 | return m_hidService; 233 | } 234 | 235 | /* 236 | * @brief 237 | */ 238 | BLEService* BLEHIDDevice::batteryService() { 239 | return m_batteryService; 240 | } 241 | 242 | #endif // CONFIG_BT_ENABLED 243 | 244 | -------------------------------------------------------------------------------- /src/BLEHIDDevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEHIDDevice.h 3 | * 4 | * Created on: Jan 03, 2018 5 | * Author: chegewara 6 | */ 7 | 8 | #ifndef _BLEHIDDEVICE_H_ 9 | #define _BLEHIDDEVICE_H_ 10 | 11 | #include "sdkconfig.h" 12 | #if defined(CONFIG_BT_ENABLED) 13 | 14 | #include "BLECharacteristic.h" 15 | #include "BLEService.h" 16 | #include "BLEDescriptor.h" 17 | #include "BLE2902.h" 18 | #include "HIDTypes.h" 19 | 20 | #define GENERIC_HID 0x03C0 21 | #define HID_KEYBOARD 0x03C1 22 | #define HID_MOUSE 0x03C2 23 | #define HID_JOYSTICK 0x03C3 24 | #define HID_GAMEPAD 0x03C4 25 | #define HID_TABLET 0x03C5 26 | #define HID_CARD_READER 0x03C6 27 | #define HID_DIGITAL_PEN 0x03C7 28 | #define HID_BARCODE 0x03C8 29 | 30 | class BLEHIDDevice { 31 | public: 32 | BLEHIDDevice(BLEServer*); 33 | virtual ~BLEHIDDevice(); 34 | 35 | void reportMap(uint8_t* map, uint16_t); 36 | void startServices(); 37 | 38 | BLEService* deviceInfo(); 39 | BLEService* hidService(); 40 | BLEService* batteryService(); 41 | 42 | BLECharacteristic* manufacturer(); 43 | void manufacturer(std::string name); 44 | //BLECharacteristic* pnp(); 45 | void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); 46 | //BLECharacteristic* hidInfo(); 47 | void hidInfo(uint8_t country, uint8_t flags); 48 | //BLECharacteristic* batteryLevel(); 49 | void setBatteryLevel(uint8_t level); 50 | 51 | 52 | //BLECharacteristic* reportMap(); 53 | BLECharacteristic* hidControl(); 54 | BLECharacteristic* inputReport(uint8_t reportID); 55 | BLECharacteristic* outputReport(uint8_t reportID); 56 | BLECharacteristic* featureReport(uint8_t reportID); 57 | BLECharacteristic* protocolMode(); 58 | BLECharacteristic* bootInput(); 59 | BLECharacteristic* bootOutput(); 60 | 61 | private: 62 | BLEService* m_deviceInfoService; //0x180a 63 | BLEService* m_hidService; //0x1812 64 | BLEService* m_batteryService = 0; //0x180f 65 | 66 | BLECharacteristic* m_manufacturerCharacteristic; //0x2a29 67 | BLECharacteristic* m_pnpCharacteristic; //0x2a50 68 | BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a 69 | BLECharacteristic* m_reportMapCharacteristic; //0x2a4b 70 | BLECharacteristic* m_hidControlCharacteristic; //0x2a4c 71 | BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e 72 | BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 73 | }; 74 | #endif // CONFIG_BT_ENABLED 75 | #endif /* _BLEHIDDEVICE_H_ */ 76 | -------------------------------------------------------------------------------- /src/BLERemoteCharacteristic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLERemoteCharacteristic.h 3 | * 4 | * Created on: Jul 8, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include "BLERemoteService.h" 18 | #include "BLERemoteDescriptor.h" 19 | #include "BLEUUID.h" 20 | #include "FreeRTOS.h" 21 | 22 | class BLERemoteService; 23 | class BLERemoteDescriptor; 24 | typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); 25 | 26 | /** 27 | * @brief A model of a remote %BLE characteristic. 28 | */ 29 | class BLERemoteCharacteristic { 30 | public: 31 | ~BLERemoteCharacteristic(); 32 | 33 | // Public member functions 34 | bool canBroadcast(); 35 | bool canIndicate(); 36 | bool canNotify(); 37 | bool canRead(); 38 | bool canWrite(); 39 | bool canWriteNoResponse(); 40 | BLERemoteDescriptor* getDescriptor(BLEUUID uuid); 41 | std::map* getDescriptors(); 42 | uint16_t getHandle(); 43 | BLEUUID getUUID(); 44 | std::string readValue(); 45 | uint8_t readUInt8(); 46 | uint16_t readUInt16(); 47 | uint32_t readUInt32(); 48 | void registerForNotify(notify_callback _callback, bool notifications = true); 49 | void writeValue(uint8_t* data, size_t length, bool response = false); 50 | void writeValue(std::string newValue, bool response = false); 51 | void writeValue(uint8_t newValue, bool response = false); 52 | std::string toString(); 53 | uint8_t* readRawData(); 54 | 55 | private: 56 | BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); 57 | friend class BLEClient; 58 | friend class BLERemoteService; 59 | friend class BLERemoteDescriptor; 60 | 61 | // Private member functions 62 | void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); 63 | 64 | BLERemoteService* getRemoteService(); 65 | void removeDescriptors(); 66 | void retrieveDescriptors(); 67 | 68 | // Private properties 69 | BLEUUID m_uuid; 70 | esp_gatt_char_prop_t m_charProp; 71 | uint16_t m_handle; 72 | BLERemoteService* m_pRemoteService; 73 | FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); 74 | FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); 75 | FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); 76 | std::string m_value; 77 | uint8_t *m_rawData; 78 | notify_callback m_notifyCallback; 79 | 80 | // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. 81 | std::map m_descriptorMap; 82 | }; // BLERemoteCharacteristic 83 | #endif /* CONFIG_BT_ENABLED */ 84 | #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ 85 | -------------------------------------------------------------------------------- /src/BLERemoteDescriptor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLERemoteDescriptor.cpp 3 | * 4 | * Created on: Jul 8, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include "BLERemoteDescriptor.h" 11 | #include "GeneralUtils.h" 12 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 13 | #include "esp32-hal-log.h" 14 | #define LOG_TAG "" 15 | #else 16 | #include "esp_log.h" 17 | static const char* LOG_TAG = "BLERemoteDescriptor"; 18 | #endif 19 | 20 | 21 | 22 | 23 | BLERemoteDescriptor::BLERemoteDescriptor( 24 | uint16_t handle, 25 | BLEUUID uuid, 26 | BLERemoteCharacteristic* pRemoteCharacteristic) { 27 | 28 | m_handle = handle; 29 | m_uuid = uuid; 30 | m_pRemoteCharacteristic = pRemoteCharacteristic; 31 | } 32 | 33 | 34 | /** 35 | * @brief Retrieve the handle associated with this remote descriptor. 36 | * @return The handle associated with this remote descriptor. 37 | */ 38 | uint16_t BLERemoteDescriptor::getHandle() { 39 | return m_handle; 40 | } // getHandle 41 | 42 | 43 | /** 44 | * @brief Get the characteristic that owns this descriptor. 45 | * @return The characteristic that owns this descriptor. 46 | */ 47 | BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { 48 | return m_pRemoteCharacteristic; 49 | } // getRemoteCharacteristic 50 | 51 | 52 | /** 53 | * @brief Retrieve the UUID associated this remote descriptor. 54 | * @return The UUID associated this remote descriptor. 55 | */ 56 | BLEUUID BLERemoteDescriptor::getUUID() { 57 | return m_uuid; 58 | } // getUUID 59 | 60 | 61 | std::string BLERemoteDescriptor::readValue() { 62 | ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); 63 | 64 | // Check to see that we are connected. 65 | if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { 66 | ESP_LOGE(LOG_TAG, "Disconnected"); 67 | throw BLEDisconnectedException(); 68 | } 69 | 70 | m_semaphoreReadDescrEvt.take("readValue"); 71 | 72 | // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. 73 | esp_err_t errRc = ::esp_ble_gattc_read_char_descr( 74 | m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), 75 | m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server 76 | getHandle(), // The handle of this characteristic 77 | ESP_GATT_AUTH_REQ_NONE); // Security 78 | 79 | if (errRc != ESP_OK) { 80 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 81 | return ""; 82 | } 83 | 84 | // Block waiting for the event that indicates that the read has completed. When it has, the std::string found 85 | // in m_value will contain our data. 86 | m_semaphoreReadDescrEvt.wait("readValue"); 87 | 88 | ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); 89 | return m_value; 90 | } // readValue 91 | 92 | 93 | uint8_t BLERemoteDescriptor::readUInt8() { 94 | std::string value = readValue(); 95 | if (value.length() >= 1) { 96 | return (uint8_t) value[0]; 97 | } 98 | return 0; 99 | } // readUInt8 100 | 101 | 102 | uint16_t BLERemoteDescriptor::readUInt16() { 103 | std::string value = readValue(); 104 | if (value.length() >= 2) { 105 | return *(uint16_t*) value.data(); 106 | } 107 | return 0; 108 | } // readUInt16 109 | 110 | 111 | uint32_t BLERemoteDescriptor::readUInt32() { 112 | std::string value = readValue(); 113 | if (value.length() >= 4) { 114 | return *(uint32_t*) value.data(); 115 | } 116 | return 0; 117 | } // readUInt32 118 | 119 | 120 | /** 121 | * @brief Return a string representation of this BLE Remote Descriptor. 122 | * @retun A string representation of this BLE Remote Descriptor. 123 | */ 124 | std::string BLERemoteDescriptor::toString() { 125 | std::stringstream ss; 126 | ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); 127 | return ss.str(); 128 | } // toString 129 | 130 | 131 | /** 132 | * @brief Write data to the BLE Remote Descriptor. 133 | * @param [in] data The data to send to the remote descriptor. 134 | * @param [in] length The length of the data to send. 135 | * @param [in] response True if we expect a response. 136 | */ 137 | void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) { 138 | ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); 139 | // Check to see that we are connected. 140 | if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { 141 | ESP_LOGE(LOG_TAG, "Disconnected"); 142 | throw BLEDisconnectedException(); 143 | } 144 | 145 | esp_err_t errRc = ::esp_ble_gattc_write_char_descr( 146 | m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), 147 | m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), 148 | getHandle(), 149 | length, // Data length 150 | data, // Data 151 | response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, 152 | ESP_GATT_AUTH_REQ_NONE 153 | ); 154 | if (errRc != ESP_OK) { 155 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); 156 | } 157 | ESP_LOGD(LOG_TAG, "<< writeValue"); 158 | } // writeValue 159 | 160 | 161 | /** 162 | * @brief Write data represented as a string to the BLE Remote Descriptor. 163 | * @param [in] newValue The data to send to the remote descriptor. 164 | * @param [in] response True if we expect a response. 165 | */ 166 | void BLERemoteDescriptor::writeValue(std::string newValue, bool response) { 167 | writeValue((uint8_t*) newValue.data(), newValue.length(), response); 168 | } // writeValue 169 | 170 | 171 | /** 172 | * @brief Write a byte value to the Descriptor. 173 | * @param [in] The single byte to write. 174 | * @param [in] True if we expect a response. 175 | */ 176 | void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { 177 | writeValue(&newValue, 1, response); 178 | } // writeValue 179 | 180 | 181 | #endif /* CONFIG_BT_ENABLED */ 182 | -------------------------------------------------------------------------------- /src/BLERemoteDescriptor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLERemoteDescriptor.h 3 | * 4 | * Created on: Jul 8, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | 14 | #include 15 | 16 | #include "BLERemoteCharacteristic.h" 17 | #include "BLEUUID.h" 18 | #include "FreeRTOS.h" 19 | 20 | class BLERemoteCharacteristic; 21 | /** 22 | * @brief A model of remote %BLE descriptor. 23 | */ 24 | class BLERemoteDescriptor { 25 | public: 26 | uint16_t getHandle(); 27 | BLERemoteCharacteristic* getRemoteCharacteristic(); 28 | BLEUUID getUUID(); 29 | std::string readValue(void); 30 | uint8_t readUInt8(void); 31 | uint16_t readUInt16(void); 32 | uint32_t readUInt32(void); 33 | std::string toString(void); 34 | void writeValue(uint8_t* data, size_t length, bool response = false); 35 | void writeValue(std::string newValue, bool response = false); 36 | void writeValue(uint8_t newValue, bool response = false); 37 | 38 | 39 | private: 40 | friend class BLERemoteCharacteristic; 41 | BLERemoteDescriptor( 42 | uint16_t handle, 43 | BLEUUID uuid, 44 | BLERemoteCharacteristic* pRemoteCharacteristic 45 | ); 46 | uint16_t m_handle; // Server handle of this descriptor. 47 | BLEUUID m_uuid; // UUID of this descriptor. 48 | std::string m_value; // Last received value of the descriptor. 49 | BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. 50 | FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); 51 | 52 | 53 | }; 54 | #endif /* CONFIG_BT_ENABLED */ 55 | #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ 56 | -------------------------------------------------------------------------------- /src/BLERemoteService.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLERemoteService.cpp 3 | * 4 | * Created on: Jul 8, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | 10 | #include 11 | #include "BLERemoteService.h" 12 | #include "BLEUtils.h" 13 | #include "GeneralUtils.h" 14 | #include 15 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 16 | #include "esp32-hal-log.h" 17 | #define LOG_TAG "" 18 | #else 19 | #include "esp_log.h" 20 | static const char* LOG_TAG = "BLERemoteService"; 21 | #endif 22 | 23 | 24 | 25 | BLERemoteService::BLERemoteService( 26 | esp_gatt_id_t srvcId, 27 | BLEClient* pClient, 28 | uint16_t startHandle, 29 | uint16_t endHandle 30 | ) { 31 | 32 | ESP_LOGD(LOG_TAG, ">> BLERemoteService()"); 33 | m_srvcId = srvcId; 34 | m_pClient = pClient; 35 | m_uuid = BLEUUID(m_srvcId); 36 | m_haveCharacteristics = false; 37 | m_startHandle = startHandle; 38 | m_endHandle = endHandle; 39 | 40 | ESP_LOGD(LOG_TAG, "<< BLERemoteService()"); 41 | } 42 | 43 | 44 | BLERemoteService::~BLERemoteService() { 45 | removeCharacteristics(); 46 | } 47 | 48 | /* 49 | static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { 50 | if (id1.id.inst_id != id2.id.inst_id) { 51 | return false; 52 | } 53 | if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) { 54 | return false; 55 | } 56 | return true; 57 | } // compareSrvcId 58 | */ 59 | 60 | /** 61 | * @brief Handle GATT Client events 62 | */ 63 | void BLERemoteService::gattClientEventHandler( 64 | esp_gattc_cb_event_t event, 65 | esp_gatt_if_t gattc_if, 66 | esp_ble_gattc_cb_param_t* evtParam) { 67 | switch (event) { 68 | // 69 | // ESP_GATTC_GET_CHAR_EVT 70 | // 71 | // get_char: 72 | // - esp_gatt_status_t status 73 | // - uin1t6_t conn_id 74 | // - esp_gatt_srvc_id_t srvc_id 75 | // - esp_gatt_id_t char_id 76 | // - esp_gatt_char_prop_t char_prop 77 | // 78 | /* 79 | case ESP_GATTC_GET_CHAR_EVT: { 80 | // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be 81 | // the same. 82 | if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) { 83 | break; 84 | } 85 | 86 | // If the status is NOT OK then we have a problem and continue. 87 | if (evtParam->get_char.status != ESP_GATT_OK) { 88 | m_semaphoreGetCharEvt.give(); 89 | break; 90 | } 91 | 92 | // This is an indication that we now have the characteristic details for a characteristic owned 93 | // by this service so remember it. 94 | m_characteristicMap.insert(std::pair( 95 | BLEUUID(evtParam->get_char.char_id.uuid).toString(), 96 | new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) )); 97 | 98 | 99 | // Now that we have received a characteristic, lets ask for the next one. 100 | esp_err_t errRc = ::esp_ble_gattc_get_characteristic( 101 | m_pClient->getGattcIf(), 102 | m_pClient->getConnId(), 103 | &m_srvcId, 104 | &evtParam->get_char.char_id); 105 | if (errRc != ESP_OK) { 106 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 107 | break; 108 | } 109 | 110 | //m_semaphoreGetCharEvt.give(); 111 | break; 112 | } // ESP_GATTC_GET_CHAR_EVT 113 | */ 114 | default: 115 | break; 116 | } // switch 117 | 118 | // Send the event to each of the characteristics owned by this service. 119 | for (auto &myPair : m_characteristicMapByHandle) { 120 | myPair.second->gattClientEventHandler(event, gattc_if, evtParam); 121 | } 122 | } // gattClientEventHandler 123 | 124 | 125 | /** 126 | * @brief Get the remote characteristic object for the characteristic UUID. 127 | * @param [in] uuid Remote characteristic uuid. 128 | * @return Reference to the remote characteristic object. 129 | * @throws BLEUuidNotFoundException 130 | */ 131 | BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { 132 | return getCharacteristic(BLEUUID(uuid)); 133 | } // getCharacteristic 134 | 135 | /** 136 | * @brief Get the characteristic object for the UUID. 137 | * @param [in] uuid Characteristic uuid. 138 | * @return Reference to the characteristic object. 139 | * @throws BLEUuidNotFoundException 140 | */ 141 | BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { 142 | // Design 143 | // ------ 144 | // We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the 145 | // device what characteristics it has in which case we have nothing to match against. If we have not 146 | // asked the device about its characteristics, then we do that now. Once we get the results we can then 147 | // examine the characteristics map to see if it has the characteristic we are looking for. 148 | if (!m_haveCharacteristics) { 149 | retrieveCharacteristics(); 150 | } 151 | std::string v = uuid.toString(); 152 | for (auto &myPair : m_characteristicMap) { 153 | if (myPair.first == v) { 154 | return myPair.second; 155 | } 156 | } 157 | // throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another 158 | return nullptr; 159 | } // getCharacteristic 160 | 161 | 162 | /** 163 | * @brief Retrieve all the characteristics for this service. 164 | * This function will not return until we have all the characteristics. 165 | * @return N/A 166 | */ 167 | void BLERemoteService::retrieveCharacteristics() { 168 | ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); 169 | 170 | removeCharacteristics(); // Forget any previous characteristics. 171 | 172 | uint16_t offset = 0; 173 | esp_gattc_char_elem_t result; 174 | while (true) { 175 | uint16_t count = 10; // this value is used as in parameter that allows to search max 10 chars with the same uuid 176 | esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( 177 | getClient()->getGattcIf(), 178 | getClient()->getConnId(), 179 | m_startHandle, 180 | m_endHandle, 181 | &result, 182 | &count, 183 | offset 184 | ); 185 | 186 | if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. 187 | break; 188 | } 189 | 190 | if (status != ESP_GATT_OK) { // If we got an error, end. 191 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str()); 192 | break; 193 | } 194 | 195 | if (count == 0) { // If we failed to get any new records, end. 196 | break; 197 | } 198 | 199 | ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str()); 200 | 201 | // We now have a new characteristic ... let us add that to our set of known characteristics 202 | BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic( 203 | result.char_handle, 204 | BLEUUID(result.uuid), 205 | result.properties, 206 | this 207 | ); 208 | 209 | m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); 210 | m_characteristicMapByHandle.insert(std::pair(result.char_handle, pNewRemoteCharacteristic)); 211 | offset++; // Increment our count of number of descriptors found. 212 | } // Loop forever (until we break inside the loop). 213 | 214 | m_haveCharacteristics = true; // Remember that we have received the characteristics. 215 | ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); 216 | } // getCharacteristics 217 | 218 | 219 | /** 220 | * @brief Retrieve a map of all the characteristics of this service. 221 | * @return A map of all the characteristics of this service. 222 | */ 223 | std::map* BLERemoteService::getCharacteristics() { 224 | ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); 225 | // If is possible that we have not read the characteristics associated with the service so do that 226 | // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking 227 | // call and does not return until all the characteristics are available. 228 | if (!m_haveCharacteristics) { 229 | retrieveCharacteristics(); 230 | } 231 | ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str()); 232 | return &m_characteristicMap; 233 | } // getCharacteristics 234 | 235 | /** 236 | * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID 237 | */ 238 | void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { 239 | #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" 240 | pCharacteristicMap = &m_characteristicMapByHandle; 241 | } // Get the characteristics map. 242 | 243 | /** 244 | * @brief Get the client associated with this service. 245 | * @return A reference to the client associated with this service. 246 | */ 247 | BLEClient* BLERemoteService::getClient() { 248 | return m_pClient; 249 | } // getClient 250 | 251 | 252 | uint16_t BLERemoteService::getEndHandle() { 253 | return m_endHandle; 254 | } // getEndHandle 255 | 256 | 257 | esp_gatt_id_t* BLERemoteService::getSrvcId() { 258 | return &m_srvcId; 259 | } // getSrvcId 260 | 261 | 262 | uint16_t BLERemoteService::getStartHandle() { 263 | return m_startHandle; 264 | } // getStartHandle 265 | 266 | 267 | uint16_t BLERemoteService::getHandle() { 268 | ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str()); 269 | ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle()); 270 | return getStartHandle(); 271 | } // getHandle 272 | 273 | 274 | BLEUUID BLERemoteService::getUUID() { 275 | return m_uuid; 276 | } 277 | 278 | /** 279 | * @brief Read the value of a characteristic associated with this service. 280 | */ 281 | std::string BLERemoteService::getValue(BLEUUID characteristicUuid) { 282 | ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); 283 | std::string ret = getCharacteristic(characteristicUuid)->readValue(); 284 | ESP_LOGD(LOG_TAG, "<< readValue"); 285 | return ret; 286 | } // readValue 287 | 288 | 289 | 290 | /** 291 | * @brief Delete the characteristics in the characteristics map. 292 | * We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic 293 | * object references. Since we allocated these in this class, we are also responsible for deleteing 294 | * them. This method does just that. 295 | * @return N/A. 296 | */ 297 | void BLERemoteService::removeCharacteristics() { 298 | for (auto &myPair : m_characteristicMap) { 299 | delete myPair.second; 300 | //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear 301 | } 302 | m_characteristicMap.clear(); // Clear the map 303 | for (auto &myPair : m_characteristicMapByHandle) { 304 | delete myPair.second; 305 | } 306 | m_characteristicMapByHandle.clear(); // Clear the map 307 | } // removeCharacteristics 308 | 309 | 310 | /** 311 | * @brief Set the value of a characteristic. 312 | * @param [in] characteristicUuid The characteristic to set. 313 | * @param [in] value The value to set. 314 | * @throws BLEUuidNotFound 315 | */ 316 | void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) { 317 | ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); 318 | getCharacteristic(characteristicUuid)->writeValue(value); 319 | ESP_LOGD(LOG_TAG, "<< setValue"); 320 | } // setValue 321 | 322 | 323 | /** 324 | * @brief Create a string representation of this remote service. 325 | * @return A string representation of this remote service. 326 | */ 327 | std::string BLERemoteService::toString() { 328 | std::ostringstream ss; 329 | ss << "Service: uuid: " + m_uuid.toString(); 330 | ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle << 331 | ", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle; 332 | for (auto &myPair : m_characteristicMap) { 333 | ss << "\n" << myPair.second->toString(); 334 | // myPair.second is the value 335 | } 336 | return ss.str(); 337 | } // toString 338 | 339 | 340 | #endif /* CONFIG_BT_ENABLED */ 341 | -------------------------------------------------------------------------------- /src/BLERemoteService.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLERemoteService.h 3 | * 4 | * Created on: Jul 8, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include 14 | 15 | #include "BLEClient.h" 16 | #include "BLERemoteCharacteristic.h" 17 | #include "BLEUUID.h" 18 | #include "FreeRTOS.h" 19 | 20 | class BLEClient; 21 | class BLERemoteCharacteristic; 22 | 23 | 24 | /** 25 | * @brief A model of a remote %BLE service. 26 | */ 27 | class BLERemoteService { 28 | public: 29 | virtual ~BLERemoteService(); 30 | 31 | // Public methods 32 | BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. 33 | BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. 34 | BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. 35 | std::map* getCharacteristics(); 36 | std::map* getCharacteristicsByHandle(); // Get the characteristics map. 37 | void getCharacteristics(std::map* pCharacteristicMap); 38 | 39 | BLEClient* getClient(void); // Get a reference to the client associated with this service. 40 | uint16_t getHandle(); // Get the handle of this service. 41 | BLEUUID getUUID(void); // Get the UUID of this service. 42 | std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic. 43 | void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic. 44 | std::string toString(void); 45 | 46 | private: 47 | // Private constructor ... never meant to be created by a user application. 48 | BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle); 49 | 50 | // Friends 51 | friend class BLEClient; 52 | friend class BLERemoteCharacteristic; 53 | 54 | // Private methods 55 | void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server. 56 | esp_gatt_id_t* getSrvcId(void); 57 | uint16_t getStartHandle(); // Get the start handle for this service. 58 | uint16_t getEndHandle(); // Get the end handle for this service. 59 | 60 | void gattClientEventHandler( 61 | esp_gattc_cb_event_t event, 62 | esp_gatt_if_t gattc_if, 63 | esp_ble_gattc_cb_param_t* evtParam); 64 | 65 | void removeCharacteristics(); 66 | 67 | // Properties 68 | 69 | // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. 70 | std::map m_characteristicMap; 71 | 72 | // We maintain a map of characteristics owned by this service keyed by a handle. 73 | std::map m_characteristicMapByHandle; 74 | 75 | bool m_haveCharacteristics; // Have we previously obtained the characteristics. 76 | BLEClient* m_pClient; 77 | FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); 78 | esp_gatt_id_t m_srvcId; 79 | BLEUUID m_uuid; // The UUID of this service. 80 | uint16_t m_startHandle; // The starting handle of this service. 81 | uint16_t m_endHandle; // The ending handle of this service. 82 | }; // BLERemoteService 83 | 84 | #endif /* CONFIG_BT_ENABLED */ 85 | #endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */ 86 | -------------------------------------------------------------------------------- /src/BLEScan.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEScan.cpp 3 | * 4 | * Created on: Jul 1, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include "BLEAdvertisedDevice.h" 16 | #include "BLEScan.h" 17 | #include "BLEUtils.h" 18 | #include "GeneralUtils.h" 19 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 20 | #include "esp32-hal-log.h" 21 | #define LOG_TAG "" 22 | #else 23 | #include "esp_log.h" 24 | static const char* LOG_TAG = "BLEScan"; 25 | #endif 26 | 27 | 28 | 29 | 30 | /** 31 | * Constructor 32 | */ 33 | BLEScan::BLEScan() { 34 | m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. 35 | m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; 36 | m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; 37 | m_pAdvertisedDeviceCallbacks = nullptr; 38 | m_stopped = true; 39 | m_wantDuplicates = false; 40 | setInterval(100); 41 | setWindow(100); 42 | } // BLEScan 43 | 44 | 45 | /** 46 | * @brief Handle GAP events related to scans. 47 | * @param [in] event The event type for this event. 48 | * @param [in] param Parameter data for this event. 49 | */ 50 | void BLEScan::handleGAPEvent( 51 | esp_gap_ble_cb_event_t event, 52 | esp_ble_gap_cb_param_t* param) { 53 | 54 | switch(event) { 55 | 56 | // --------------------------- 57 | // scan_rst: 58 | // esp_gap_search_evt_t search_evt 59 | // esp_bd_addr_t bda 60 | // esp_bt_dev_type_t dev_type 61 | // esp_ble_addr_type_t ble_addr_type 62 | // esp_ble_evt_type_t ble_evt_type 63 | // int rssi 64 | // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] 65 | // int flag 66 | // int num_resps 67 | // uint8_t adv_data_len 68 | // uint8_t scan_rsp_len 69 | case ESP_GAP_BLE_SCAN_RESULT_EVT: { 70 | 71 | switch(param->scan_rst.search_evt) { 72 | // 73 | // ESP_GAP_SEARCH_INQ_CMPL_EVT 74 | // 75 | // Event that indicates that the duration allowed for the search has completed or that we have been 76 | // asked to stop. 77 | case ESP_GAP_SEARCH_INQ_CMPL_EVT: { 78 | ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT"); 79 | m_stopped = true; 80 | m_semaphoreScanEnd.give(); 81 | if (m_scanCompleteCB != nullptr) { 82 | m_scanCompleteCB(m_scanResults); 83 | } 84 | break; 85 | } // ESP_GAP_SEARCH_INQ_CMPL_EVT 86 | 87 | // 88 | // ESP_GAP_SEARCH_INQ_RES_EVT 89 | // 90 | // Result that has arrived back from a Scan inquiry. 91 | case ESP_GAP_SEARCH_INQ_RES_EVT: { 92 | if (m_stopped) { // If we are not scanning, nothing to do with the extra results. 93 | break; 94 | } 95 | 96 | // Examine our list of previously scanned addresses and, if we found this one already, 97 | // ignore it. 98 | BLEAddress advertisedAddress(param->scan_rst.bda); 99 | bool found = false; 100 | 101 | if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) { 102 | found = true; 103 | } 104 | 105 | if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. 106 | ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str()); 107 | vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here 108 | break; 109 | } 110 | 111 | // We now construct a model of the advertised device that we have just found for the first 112 | // time. 113 | // ESP_LOG_BUFFER_HEXDUMP(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG); 114 | // ESP_LOGW(LOG_TAG, "bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type); 115 | BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice(); 116 | advertisedDevice->setAddress(advertisedAddress); 117 | advertisedDevice->setRSSI(param->scan_rst.rssi); 118 | advertisedDevice->setAdFlag(param->scan_rst.flag); 119 | advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len); 120 | advertisedDevice->setScan(this); 121 | advertisedDevice->setAddressType(param->scan_rst.ble_addr_type); 122 | 123 | if (!found) { // If we have previously seen this device, don't record it again. 124 | m_scanResults.m_vectorAdvertisedDevices.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); 125 | } 126 | 127 | if (m_pAdvertisedDeviceCallbacks) { 128 | m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); 129 | } 130 | if(found) 131 | delete advertisedDevice; 132 | 133 | break; 134 | } // ESP_GAP_SEARCH_INQ_RES_EVT 135 | 136 | default: { 137 | break; 138 | } 139 | } // switch - search_evt 140 | 141 | 142 | break; 143 | } // ESP_GAP_BLE_SCAN_RESULT_EVT 144 | 145 | default: { 146 | break; 147 | } // default 148 | } // End switch 149 | } // gapEventHandler 150 | 151 | 152 | /** 153 | * @brief Should we perform an active or passive scan? 154 | * The default is a passive scan. An active scan means that we will wish a scan response. 155 | * @param [in] active If true, we perform an active scan otherwise a passive scan. 156 | * @return N/A. 157 | */ 158 | void BLEScan::setActiveScan(bool active) { 159 | if (active) { 160 | m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; 161 | } else { 162 | m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; 163 | } 164 | } // setActiveScan 165 | 166 | 167 | /** 168 | * @brief Set the call backs to be invoked. 169 | * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. 170 | * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. 171 | */ 172 | void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { 173 | m_wantDuplicates = wantDuplicates; 174 | m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; 175 | } // setAdvertisedDeviceCallbacks 176 | 177 | 178 | /** 179 | * @brief Set the interval to scan. 180 | * @param [in] The interval in msecs. 181 | */ 182 | void BLEScan::setInterval(uint16_t intervalMSecs) { 183 | m_scan_params.scan_interval = intervalMSecs / 0.625; 184 | } // setInterval 185 | 186 | 187 | /** 188 | * @brief Set the window to actively scan. 189 | * @param [in] windowMSecs How long to actively scan. 190 | */ 191 | void BLEScan::setWindow(uint16_t windowMSecs) { 192 | m_scan_params.scan_window = windowMSecs / 0.625; 193 | } // setWindow 194 | 195 | 196 | /** 197 | * @brief Start scanning. 198 | * @param [in] duration The duration in seconds for which to scan. 199 | * @param [in] scanCompleteCB A function to be called when scanning has completed. 200 | * @param [in] are we continue scan (true) or we want to clear stored devices (false) 201 | * @return True if scan started or false if there was an error. 202 | */ 203 | bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) { 204 | ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); 205 | 206 | m_semaphoreScanEnd.take(std::string("start")); 207 | m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. 208 | 209 | // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals 210 | // then we should not clear map or we will connect the same device few times 211 | if(!is_continue) { 212 | for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ 213 | delete _dev.second; 214 | } 215 | m_scanResults.m_vectorAdvertisedDevices.clear(); 216 | } 217 | 218 | esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); 219 | 220 | if (errRc != ESP_OK) { 221 | ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); 222 | m_semaphoreScanEnd.give(); 223 | return false; 224 | } 225 | 226 | errRc = ::esp_ble_gap_start_scanning(duration); 227 | 228 | if (errRc != ESP_OK) { 229 | ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); 230 | m_semaphoreScanEnd.give(); 231 | return false; 232 | } 233 | 234 | m_stopped = false; 235 | 236 | ESP_LOGD(LOG_TAG, "<< start()"); 237 | return true; 238 | } // start 239 | 240 | 241 | /** 242 | * @brief Start scanning and block until scanning has been completed. 243 | * @param [in] duration The duration in seconds for which to scan. 244 | * @return The BLEScanResults. 245 | */ 246 | BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) { 247 | if(start(duration, nullptr, is_continue)) { 248 | m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. 249 | } 250 | return m_scanResults; 251 | } // start 252 | 253 | 254 | /** 255 | * @brief Stop an in progress scan. 256 | * @return N/A. 257 | */ 258 | void BLEScan::stop() { 259 | ESP_LOGD(LOG_TAG, ">> stop()"); 260 | 261 | esp_err_t errRc = ::esp_ble_gap_stop_scanning(); 262 | 263 | m_stopped = true; 264 | m_semaphoreScanEnd.give(); 265 | 266 | if (errRc != ESP_OK) { 267 | ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); 268 | return; 269 | } 270 | 271 | ESP_LOGD(LOG_TAG, "<< stop()"); 272 | } // stop 273 | 274 | // delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address 275 | void BLEScan::erase(BLEAddress address) { 276 | ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); 277 | BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second; 278 | m_scanResults.m_vectorAdvertisedDevices.erase(address.toString()); 279 | delete advertisedDevice; 280 | } 281 | 282 | 283 | /** 284 | * @brief Dump the scan results to the log. 285 | */ 286 | void BLEScanResults::dump() { 287 | ESP_LOGD(LOG_TAG, ">> Dump scan results:"); 288 | for (int i=0; isecond; 312 | for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) { 313 | dev = *it->second; 314 | if (x==i) break; 315 | x++; 316 | } 317 | return dev; 318 | } 319 | 320 | BLEScanResults BLEScan::getResults() { 321 | return m_scanResults; 322 | } 323 | 324 | void BLEScan::clearResults() { 325 | for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ 326 | delete _dev.second; 327 | } 328 | m_scanResults.m_vectorAdvertisedDevices.clear(); 329 | } 330 | 331 | #endif /* CONFIG_BT_ENABLED */ 332 | -------------------------------------------------------------------------------- /src/BLEScan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEScan.h 3 | * 4 | * Created on: Jul 1, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ 9 | #define COMPONENTS_CPP_UTILS_BLESCAN_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | 14 | // #include 15 | #include 16 | #include "BLEAdvertisedDevice.h" 17 | #include "BLEClient.h" 18 | #include "FreeRTOS.h" 19 | 20 | class BLEAdvertisedDevice; 21 | class BLEAdvertisedDeviceCallbacks; 22 | class BLEClient; 23 | class BLEScan; 24 | 25 | 26 | /** 27 | * @brief The result of having performed a scan. 28 | * When a scan completes, we have a set of found devices. Each device is described 29 | * by a BLEAdvertisedDevice object. The number of items in the set is given by 30 | * getCount(). We can retrieve a device by calling getDevice() passing in the 31 | * index (starting at 0) of the desired device. 32 | */ 33 | class BLEScanResults { 34 | public: 35 | void dump(); 36 | int getCount(); 37 | BLEAdvertisedDevice getDevice(uint32_t i); 38 | 39 | private: 40 | friend BLEScan; 41 | std::map m_vectorAdvertisedDevices; 42 | }; 43 | 44 | /** 45 | * @brief Perform and manage %BLE scans. 46 | * 47 | * Scanning is associated with a %BLE client that is attempting to locate BLE servers. 48 | */ 49 | class BLEScan { 50 | public: 51 | void setActiveScan(bool active); 52 | void setAdvertisedDeviceCallbacks( 53 | BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, 54 | bool wantDuplicates = false); 55 | void setInterval(uint16_t intervalMSecs); 56 | void setWindow(uint16_t windowMSecs); 57 | bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false); 58 | BLEScanResults start(uint32_t duration, bool is_continue = false); 59 | void stop(); 60 | void erase(BLEAddress address); 61 | BLEScanResults getResults(); 62 | void clearResults(); 63 | 64 | private: 65 | BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. 66 | friend class BLEDevice; 67 | void handleGAPEvent( 68 | esp_gap_ble_cb_event_t event, 69 | esp_ble_gap_cb_param_t* param); 70 | void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); 71 | 72 | 73 | esp_ble_scan_params_t m_scan_params; 74 | BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; 75 | bool m_stopped = true; 76 | FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); 77 | BLEScanResults m_scanResults; 78 | bool m_wantDuplicates; 79 | void (*m_scanCompleteCB)(BLEScanResults scanResults); 80 | }; // BLEScan 81 | 82 | #endif /* CONFIG_BT_ENABLED */ 83 | #endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ 84 | -------------------------------------------------------------------------------- /src/BLESecurity.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLESecurity.cpp 3 | * 4 | * Created on: Dec 17, 2017 5 | * Author: chegewara 6 | */ 7 | 8 | #include "BLESecurity.h" 9 | #include "sdkconfig.h" 10 | #if defined(CONFIG_BT_ENABLED) 11 | 12 | BLESecurity::BLESecurity() { 13 | } 14 | 15 | BLESecurity::~BLESecurity() { 16 | } 17 | /* 18 | * @brief Set requested authentication mode 19 | */ 20 | void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { 21 | m_authReq = auth_req; 22 | esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode 23 | } 24 | 25 | /** 26 | * @brief Set our device IO capability to let end user perform authorization 27 | * either by displaying or entering generated 6-digits pin code 28 | */ 29 | void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { 30 | m_iocap = iocap; 31 | esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); 32 | } // setCapability 33 | 34 | 35 | /** 36 | * @brief Init encryption key by server 37 | * @param key_size is value between 7 and 16 38 | */ 39 | void BLESecurity::setInitEncryptionKey(uint8_t init_key) { 40 | m_initKey = init_key; 41 | esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); 42 | } // setInitEncryptionKey 43 | 44 | 45 | /** 46 | * @brief Init encryption key by client 47 | * @param key_size is value between 7 and 16 48 | */ 49 | void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { 50 | m_respKey = resp_key; 51 | esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); 52 | } // setRespEncryptionKey 53 | 54 | 55 | /** 56 | * 57 | * 58 | */ 59 | void BLESecurity::setKeySize(uint8_t key_size) { 60 | m_keySize = key_size; 61 | esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); 62 | } //setKeySize 63 | 64 | 65 | /** 66 | * @brief Debug function to display what keys are exchanged by peers 67 | */ 68 | char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { 69 | char* key_str = nullptr; 70 | switch (key_type) { 71 | case ESP_LE_KEY_NONE: 72 | key_str = (char*) "ESP_LE_KEY_NONE"; 73 | break; 74 | case ESP_LE_KEY_PENC: 75 | key_str = (char*) "ESP_LE_KEY_PENC"; 76 | break; 77 | case ESP_LE_KEY_PID: 78 | key_str = (char*) "ESP_LE_KEY_PID"; 79 | break; 80 | case ESP_LE_KEY_PCSRK: 81 | key_str = (char*) "ESP_LE_KEY_PCSRK"; 82 | break; 83 | case ESP_LE_KEY_PLK: 84 | key_str = (char*) "ESP_LE_KEY_PLK"; 85 | break; 86 | case ESP_LE_KEY_LLK: 87 | key_str = (char*) "ESP_LE_KEY_LLK"; 88 | break; 89 | case ESP_LE_KEY_LENC: 90 | key_str = (char*) "ESP_LE_KEY_LENC"; 91 | break; 92 | case ESP_LE_KEY_LID: 93 | key_str = (char*) "ESP_LE_KEY_LID"; 94 | break; 95 | case ESP_LE_KEY_LCSRK: 96 | key_str = (char*) "ESP_LE_KEY_LCSRK"; 97 | break; 98 | default: 99 | key_str = (char*) "INVALID BLE KEY TYPE"; 100 | break; 101 | } 102 | return key_str; 103 | } // esp_key_type_to_str 104 | #endif // CONFIG_BT_ENABLED 105 | -------------------------------------------------------------------------------- /src/BLESecurity.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLESecurity.h 3 | * 4 | * Created on: Dec 17, 2017 5 | * Author: chegewara 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ 9 | #define COMPONENTS_CPP_UTILS_BLESECURITY_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include 14 | 15 | class BLESecurity { 16 | public: 17 | BLESecurity(); 18 | virtual ~BLESecurity(); 19 | void setAuthenticationMode(esp_ble_auth_req_t auth_req); 20 | void setCapability(esp_ble_io_cap_t iocap); 21 | void setInitEncryptionKey(uint8_t init_key); 22 | void setRespEncryptionKey(uint8_t resp_key); 23 | void setKeySize(uint8_t key_size = 16); 24 | static char* esp_key_type_to_str(esp_ble_key_type_t key_type); 25 | 26 | private: 27 | esp_ble_auth_req_t m_authReq; 28 | esp_ble_io_cap_t m_iocap; 29 | uint8_t m_initKey; 30 | uint8_t m_respKey; 31 | uint8_t m_keySize; 32 | 33 | }; // BLESecurity 34 | 35 | 36 | /* 37 | * @brief Callbacks to handle GAP events related to authorization 38 | */ 39 | class BLESecurityCallbacks { 40 | public: 41 | virtual ~BLESecurityCallbacks() {}; 42 | 43 | /** 44 | * @brief Its request from peer device to input authentication pin code displayed on peer device. 45 | * It requires that our device is capable to input 6-digits code by end user 46 | * @return Return 6-digits integer value from input device 47 | */ 48 | virtual uint32_t onPassKeyRequest() = 0; 49 | 50 | /** 51 | * @brief Provide us 6-digits code to perform authentication. 52 | * It requires that our device is capable to display this code to end user 53 | * @param 54 | */ 55 | virtual void onPassKeyNotify(uint32_t pass_key) = 0; 56 | 57 | /** 58 | * @brief Here we can make decision if we want to let negotiate authorization with peer device or not 59 | * return Return true if we accept this peer device request 60 | */ 61 | 62 | virtual bool onSecurityRequest() = 0 ; 63 | /** 64 | * Provide us information when authentication process is completed 65 | */ 66 | virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; 67 | 68 | virtual bool onConfirmPIN(uint32_t pin) = 0; 69 | }; // BLESecurityCallbacks 70 | 71 | #endif // CONFIG_BT_ENABLED 72 | #endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ 73 | -------------------------------------------------------------------------------- /src/BLEServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEServer.cpp 3 | * 4 | * Created on: Apr 16, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #include "sdkconfig.h" 9 | #if defined(CONFIG_BT_ENABLED) 10 | #include 11 | #include 12 | #include "GeneralUtils.h" 13 | #include "BLEDevice.h" 14 | #include "BLEServer.h" 15 | #include "BLEService.h" 16 | #include "BLEUtils.h" 17 | #include 18 | #include 19 | #include 20 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 21 | #include "esp32-hal-log.h" 22 | #define LOG_TAG "" 23 | #else 24 | #include "esp_log.h" 25 | static const char* LOG_TAG = "BLEServer"; 26 | #endif 27 | 28 | 29 | 30 | 31 | /** 32 | * @brief Construct a %BLE Server 33 | * 34 | * This class is not designed to be individually instantiated. Instead one should create a server by asking 35 | * the BLEDevice class. 36 | */ 37 | BLEServer::BLEServer() { 38 | m_appId = ESP_GATT_IF_NONE; 39 | m_gatts_if = ESP_GATT_IF_NONE; 40 | m_connectedCount = 0; 41 | m_connId = ESP_GATT_IF_NONE; 42 | m_pServerCallbacks = nullptr; 43 | } // BLEServer 44 | 45 | 46 | void BLEServer::createApp(uint16_t appId) { 47 | m_appId = appId; 48 | registerApp(appId); 49 | } // createApp 50 | 51 | 52 | /** 53 | * @brief Create a %BLE Service. 54 | * 55 | * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition 56 | * of a new service. Every service must have a unique UUID. 57 | * @param [in] uuid The UUID of the new service. 58 | * @return A reference to the new service object. 59 | */ 60 | BLEService* BLEServer::createService(const char* uuid) { 61 | return createService(BLEUUID(uuid)); 62 | } 63 | 64 | 65 | /** 66 | * @brief Create a %BLE Service. 67 | * 68 | * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition 69 | * of a new service. Every service must have a unique UUID. 70 | * @param [in] uuid The UUID of the new service. 71 | * @param [in] numHandles The maximum number of handles associated with this service. 72 | * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. 73 | * @return A reference to the new service object. 74 | */ 75 | BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { 76 | ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); 77 | m_semaphoreCreateEvt.take("createService"); 78 | 79 | // Check that a service with the supplied UUID does not already exist. 80 | if (m_serviceMap.getByUUID(uuid) != nullptr) { 81 | ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", 82 | uuid.toString().c_str()); 83 | } 84 | 85 | BLEService* pService = new BLEService(uuid, numHandles); 86 | pService->m_instId = inst_id; 87 | m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. 88 | pService->executeCreate(this); // Perform the API calls to actually create the service. 89 | 90 | m_semaphoreCreateEvt.wait("createService"); 91 | 92 | ESP_LOGD(LOG_TAG, "<< createService"); 93 | return pService; 94 | } // createService 95 | 96 | 97 | /** 98 | * @brief Get a %BLE Service by its UUID 99 | * @param [in] uuid The UUID of the new service. 100 | * @return A reference to the service object. 101 | */ 102 | BLEService* BLEServer::getServiceByUUID(const char* uuid) { 103 | return m_serviceMap.getByUUID(uuid); 104 | } 105 | 106 | /** 107 | * @brief Get a %BLE Service by its UUID 108 | * @param [in] uuid The UUID of the new service. 109 | * @return A reference to the service object. 110 | */ 111 | BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { 112 | return m_serviceMap.getByUUID(uuid); 113 | } 114 | 115 | /** 116 | * @brief Retrieve the advertising object that can be used to advertise the existence of the server. 117 | * 118 | * @return An advertising object. 119 | */ 120 | BLEAdvertising* BLEServer::getAdvertising() { 121 | return BLEDevice::getAdvertising(); 122 | } 123 | 124 | uint16_t BLEServer::getConnId() { 125 | return m_connId; 126 | } 127 | 128 | 129 | /** 130 | * @brief Return the number of connected clients. 131 | * @return The number of connected clients. 132 | */ 133 | uint32_t BLEServer::getConnectedCount() { 134 | return m_connectedCount; 135 | } // getConnectedCount 136 | 137 | 138 | uint16_t BLEServer::getGattsIf() { 139 | return m_gatts_if; 140 | } 141 | 142 | 143 | /** 144 | * @brief Handle a GATT Server Event. 145 | * 146 | * @param [in] event 147 | * @param [in] gatts_if 148 | * @param [in] param 149 | * 150 | */ 151 | void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { 152 | ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", 153 | BLEUtils::gattServerEventTypeToString(event).c_str()); 154 | 155 | switch(event) { 156 | // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. 157 | // add_char: 158 | // - esp_gatt_status_t status 159 | // - uint16_t attr_handle 160 | // - uint16_t service_handle 161 | // - esp_bt_uuid_t char_uuid 162 | // 163 | case ESP_GATTS_ADD_CHAR_EVT: { 164 | break; 165 | } // ESP_GATTS_ADD_CHAR_EVT 166 | 167 | case ESP_GATTS_MTU_EVT: 168 | updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); 169 | break; 170 | 171 | // ESP_GATTS_CONNECT_EVT 172 | // connect: 173 | // - uint16_t conn_id 174 | // - esp_bd_addr_t remote_bda 175 | // 176 | case ESP_GATTS_CONNECT_EVT: { 177 | m_connId = param->connect.conn_id; 178 | addPeerDevice((void*)this, false, m_connId); 179 | if (m_pServerCallbacks != nullptr) { 180 | m_pServerCallbacks->onConnect(this); 181 | m_pServerCallbacks->onConnect(this, param); 182 | } 183 | m_connectedCount++; // Increment the number of connected devices count. 184 | break; 185 | } // ESP_GATTS_CONNECT_EVT 186 | 187 | 188 | // ESP_GATTS_CREATE_EVT 189 | // Called when a new service is registered as having been created. 190 | // 191 | // create: 192 | // * esp_gatt_status_t status 193 | // * uint16_t service_handle 194 | // * esp_gatt_srvc_id_t service_id 195 | // 196 | case ESP_GATTS_CREATE_EVT: { 197 | BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid 198 | m_serviceMap.setByHandle(param->create.service_handle, pService); 199 | m_semaphoreCreateEvt.give(); 200 | break; 201 | } // ESP_GATTS_CREATE_EVT 202 | 203 | 204 | // ESP_GATTS_DISCONNECT_EVT 205 | // 206 | // disconnect 207 | // - uint16_t conn_id 208 | // - esp_bd_addr_t remote_bda 209 | // - esp_gatt_conn_reason_t reason 210 | // 211 | // If we receive a disconnect event then invoke the callback for disconnects (if one is present). 212 | // we also want to start advertising again. 213 | case ESP_GATTS_DISCONNECT_EVT: { 214 | m_connectedCount--; // Decrement the number of connected devices count. 215 | if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. 216 | m_pServerCallbacks->onDisconnect(this); 217 | } 218 | startAdvertising(); //- do this with some delay from the loop() 219 | removePeerDevice(param->disconnect.conn_id, false); 220 | break; 221 | } // ESP_GATTS_DISCONNECT_EVT 222 | 223 | 224 | // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. 225 | // 226 | // read: 227 | // - uint16_t conn_id 228 | // - uint32_t trans_id 229 | // - esp_bd_addr_t bda 230 | // - uint16_t handle 231 | // - uint16_t offset 232 | // - bool is_long 233 | // - bool need_rsp 234 | // 235 | case ESP_GATTS_READ_EVT: { 236 | break; 237 | } // ESP_GATTS_READ_EVT 238 | 239 | 240 | // ESP_GATTS_REG_EVT 241 | // reg: 242 | // - esp_gatt_status_t status 243 | // - uint16_t app_id 244 | // 245 | case ESP_GATTS_REG_EVT: { 246 | m_gatts_if = gatts_if; 247 | m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. 248 | break; 249 | } // ESP_GATTS_REG_EVT 250 | 251 | 252 | // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. 253 | // 254 | // write: 255 | // - uint16_t conn_id 256 | // - uint16_t trans_id 257 | // - esp_bd_addr_t bda 258 | // - uint16_t handle 259 | // - uint16_t offset 260 | // - bool need_rsp 261 | // - bool is_prep 262 | // - uint16_t len 263 | // - uint8_t* value 264 | // 265 | case ESP_GATTS_WRITE_EVT: { 266 | break; 267 | } 268 | 269 | case ESP_GATTS_OPEN_EVT: 270 | m_semaphoreOpenEvt.give(param->open.status); 271 | break; 272 | 273 | default: 274 | break; 275 | } 276 | 277 | // Invoke the handler for every Service we have. 278 | m_serviceMap.handleGATTServerEvent(event, gatts_if, param); 279 | 280 | ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); 281 | } // handleGATTServerEvent 282 | 283 | 284 | /** 285 | * @brief Register the app. 286 | * 287 | * @return N/A 288 | */ 289 | void BLEServer::registerApp(uint16_t m_appId) { 290 | ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); 291 | m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. 292 | ::esp_ble_gatts_app_register(m_appId); 293 | m_semaphoreRegisterAppEvt.wait("registerApp"); 294 | ESP_LOGD(LOG_TAG, "<< registerApp"); 295 | } // registerApp 296 | 297 | 298 | /** 299 | * @brief Set the server callbacks. 300 | * 301 | * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client 302 | * disconnecting. This function can be called to register a callback handler that will be invoked when these 303 | * events are detected. 304 | * 305 | * @param [in] pCallbacks The callbacks to be invoked. 306 | */ 307 | void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { 308 | m_pServerCallbacks = pCallbacks; 309 | } // setCallbacks 310 | 311 | /* 312 | * Remove service 313 | */ 314 | void BLEServer::removeService(BLEService* service) { 315 | service->stop(); 316 | service->executeDelete(); 317 | m_serviceMap.removeService(service); 318 | } 319 | 320 | /** 321 | * @brief Start advertising. 322 | * 323 | * Start the server advertising its existence. This is a convenience function and is equivalent to 324 | * retrieving the advertising object and invoking start upon it. 325 | */ 326 | void BLEServer::startAdvertising() { 327 | ESP_LOGD(LOG_TAG, ">> startAdvertising"); 328 | BLEDevice::startAdvertising(); 329 | ESP_LOGD(LOG_TAG, "<< startAdvertising"); 330 | } // startAdvertising 331 | 332 | /** 333 | * Allow to connect GATT server to peer device 334 | * Probably can be used in ANCS for iPhone 335 | */ 336 | bool BLEServer::connect(BLEAddress address) { 337 | esp_bd_addr_t addr; 338 | memcpy(&addr, address.getNative(), 6); 339 | // Perform the open connection request against the target BLE Server. 340 | m_semaphoreOpenEvt.take("connect"); 341 | esp_err_t errRc = ::esp_ble_gatts_open( 342 | getGattsIf(), 343 | addr, // address 344 | 1 // direct connection 345 | ); 346 | if (errRc != ESP_OK) { 347 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 348 | return false; 349 | } 350 | 351 | uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. 352 | ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); 353 | return rc == ESP_GATT_OK; 354 | } // connect 355 | 356 | 357 | 358 | void BLEServerCallbacks::onConnect(BLEServer* pServer) { 359 | ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); 360 | ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); 361 | ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); 362 | } // onConnect 363 | 364 | void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { 365 | ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); 366 | ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); 367 | ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); 368 | } // onConnect 369 | 370 | 371 | void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { 372 | ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); 373 | ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); 374 | ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); 375 | } // onDisconnect 376 | 377 | /* multi connect support */ 378 | /* TODO do some more tweaks */ 379 | void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { 380 | // set mtu in conn_status_t 381 | const std::map::iterator it = m_connectedServersMap.find(conn_id); 382 | if (it != m_connectedServersMap.end()) { 383 | it->second.mtu = mtu; 384 | std::swap(m_connectedServersMap[conn_id], it->second); 385 | } 386 | } 387 | 388 | std::map BLEServer::getPeerDevices(bool _client) { 389 | return m_connectedServersMap; 390 | } 391 | 392 | 393 | uint16_t BLEServer::getPeerMTU(uint16_t conn_id) { 394 | return m_connectedServersMap.find(conn_id)->second.mtu; 395 | } 396 | 397 | void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { 398 | conn_status_t status = { 399 | .peer_device = peer, 400 | .connected = true, 401 | .mtu = 23 402 | }; 403 | 404 | m_connectedServersMap.insert(std::pair(conn_id, status)); 405 | } 406 | 407 | void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { 408 | m_connectedServersMap.erase(conn_id); 409 | } 410 | /* multi connect support */ 411 | 412 | /** 413 | * Update connection parameters can be called only after connection has been established 414 | */ 415 | void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { 416 | esp_ble_conn_update_params_t conn_params; 417 | memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); 418 | conn_params.latency = latency; 419 | conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms 420 | conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms 421 | conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms 422 | esp_ble_gap_update_conn_params(&conn_params); 423 | } 424 | #endif // CONFIG_BT_ENABLED 425 | -------------------------------------------------------------------------------- /src/BLEServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEServer.h 3 | * 4 | * Created on: Apr 16, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_ 9 | #define COMPONENTS_CPP_UTILS_BLESERVER_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | 14 | #include 15 | #include 16 | // #include "BLEDevice.h" 17 | 18 | #include "BLEUUID.h" 19 | #include "BLEAdvertising.h" 20 | #include "BLECharacteristic.h" 21 | #include "BLEService.h" 22 | #include "BLESecurity.h" 23 | #include "FreeRTOS.h" 24 | #include "BLEAddress.h" 25 | 26 | class BLEServerCallbacks; 27 | /* TODO possibly refactor this struct */ 28 | typedef struct { 29 | void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here 30 | bool connected; // do we need it? 31 | uint16_t mtu; // every peer device negotiate own mtu 32 | } conn_status_t; 33 | 34 | 35 | /** 36 | * @brief A data structure that manages the %BLE servers owned by a BLE server. 37 | */ 38 | class BLEServiceMap { 39 | public: 40 | BLEService* getByHandle(uint16_t handle); 41 | BLEService* getByUUID(const char* uuid); 42 | BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); 43 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); 44 | void setByHandle(uint16_t handle, BLEService* service); 45 | void setByUUID(const char* uuid, BLEService* service); 46 | void setByUUID(BLEUUID uuid, BLEService* service); 47 | std::string toString(); 48 | BLEService* getFirst(); 49 | BLEService* getNext(); 50 | void removeService(BLEService *service); 51 | int getRegisteredServiceCount(); 52 | 53 | private: 54 | std::map m_handleMap; 55 | std::map m_uuidMap; 56 | std::map::iterator m_iterator; 57 | }; 58 | 59 | 60 | /** 61 | * @brief The model of a %BLE server. 62 | */ 63 | class BLEServer { 64 | public: 65 | uint32_t getConnectedCount(); 66 | BLEService* createService(const char* uuid); 67 | BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); 68 | BLEAdvertising* getAdvertising(); 69 | void setCallbacks(BLEServerCallbacks* pCallbacks); 70 | void startAdvertising(); 71 | void removeService(BLEService* service); 72 | BLEService* getServiceByUUID(const char* uuid); 73 | BLEService* getServiceByUUID(BLEUUID uuid); 74 | bool connect(BLEAddress address); 75 | uint16_t m_appId; 76 | void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); 77 | 78 | /* multi connection support */ 79 | std::map getPeerDevices(bool client); 80 | void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); 81 | void removePeerDevice(uint16_t conn_id, bool client); 82 | BLEServer* getServerByConnId(uint16_t conn_id); 83 | void updatePeerMTU(uint16_t connId, uint16_t mtu); 84 | uint16_t getPeerMTU(uint16_t conn_id); 85 | uint16_t getConnId(); 86 | 87 | 88 | private: 89 | BLEServer(); 90 | friend class BLEService; 91 | friend class BLECharacteristic; 92 | friend class BLEDevice; 93 | esp_ble_adv_data_t m_adv_data; 94 | // BLEAdvertising m_bleAdvertising; 95 | uint16_t m_connId; 96 | uint32_t m_connectedCount; 97 | uint16_t m_gatts_if; 98 | std::map m_connectedServersMap; 99 | 100 | FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); 101 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 102 | FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); 103 | BLEServiceMap m_serviceMap; 104 | BLEServerCallbacks* m_pServerCallbacks = nullptr; 105 | 106 | void createApp(uint16_t appId); 107 | uint16_t getGattsIf(); 108 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); 109 | void registerApp(uint16_t); 110 | }; // BLEServer 111 | 112 | 113 | /** 114 | * @brief Callbacks associated with the operation of a %BLE server. 115 | */ 116 | class BLEServerCallbacks { 117 | public: 118 | virtual ~BLEServerCallbacks() {}; 119 | /** 120 | * @brief Handle a new client connection. 121 | * 122 | * When a new client connects, we are invoked. 123 | * 124 | * @param [in] pServer A reference to the %BLE server that received the client connection. 125 | */ 126 | virtual void onConnect(BLEServer* pServer); 127 | virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); 128 | /** 129 | * @brief Handle an existing client disconnection. 130 | * 131 | * When an existing client disconnects, we are invoked. 132 | * 133 | * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. 134 | */ 135 | virtual void onDisconnect(BLEServer* pServer); 136 | }; // BLEServerCallbacks 137 | 138 | 139 | #endif /* CONFIG_BT_ENABLED */ 140 | #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ 141 | -------------------------------------------------------------------------------- /src/BLEService.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEService.h 3 | * 4 | * Created on: Mar 25, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ 9 | #define COMPONENTS_CPP_UTILS_BLESERVICE_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | 13 | #include 14 | 15 | #include "BLECharacteristic.h" 16 | #include "BLEServer.h" 17 | #include "BLEUUID.h" 18 | #include "FreeRTOS.h" 19 | 20 | class BLEServer; 21 | 22 | /** 23 | * @brief A data mapping used to manage the set of %BLE characteristics known to the server. 24 | */ 25 | class BLECharacteristicMap { 26 | public: 27 | void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); 28 | void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); 29 | void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); 30 | BLECharacteristic* getByUUID(const char* uuid); 31 | BLECharacteristic* getByUUID(BLEUUID uuid); 32 | BLECharacteristic* getByHandle(uint16_t handle); 33 | BLECharacteristic* getFirst(); 34 | BLECharacteristic* getNext(); 35 | std::string toString(); 36 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); 37 | 38 | private: 39 | std::map m_uuidMap; 40 | std::map m_handleMap; 41 | std::map::iterator m_iterator; 42 | }; 43 | 44 | 45 | /** 46 | * @brief The model of a %BLE service. 47 | * 48 | */ 49 | class BLEService { 50 | public: 51 | void addCharacteristic(BLECharacteristic* pCharacteristic); 52 | BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); 53 | BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); 54 | void dump(); 55 | void executeCreate(BLEServer* pServer); 56 | void executeDelete(); 57 | BLECharacteristic* getCharacteristic(const char* uuid); 58 | BLECharacteristic* getCharacteristic(BLEUUID uuid); 59 | BLEUUID getUUID(); 60 | BLEServer* getServer(); 61 | void start(); 62 | void stop(); 63 | std::string toString(); 64 | uint16_t getHandle(); 65 | uint8_t m_instId = 0; 66 | 67 | private: 68 | BLEService(const char* uuid, uint16_t numHandles); 69 | BLEService(BLEUUID uuid, uint16_t numHandles); 70 | friend class BLEServer; 71 | friend class BLEServiceMap; 72 | friend class BLEDescriptor; 73 | friend class BLECharacteristic; 74 | friend class BLEDevice; 75 | 76 | BLECharacteristicMap m_characteristicMap; 77 | uint16_t m_handle; 78 | BLECharacteristic* m_lastCreatedCharacteristic = nullptr; 79 | BLEServer* m_pServer = nullptr; 80 | BLEUUID m_uuid; 81 | 82 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 83 | FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); 84 | FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); 85 | FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); 86 | 87 | uint16_t m_numHandles; 88 | 89 | BLECharacteristic* getLastCreatedCharacteristic(); 90 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); 91 | void setHandle(uint16_t handle); 92 | //void setService(esp_gatt_srvc_id_t srvc_id); 93 | }; // BLEService 94 | 95 | 96 | #endif // CONFIG_BT_ENABLED 97 | #endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ 98 | -------------------------------------------------------------------------------- /src/BLEServiceMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEServiceMap.cpp 3 | * 4 | * Created on: Jun 22, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include "BLEService.h" 12 | 13 | 14 | /** 15 | * @brief Return the service by UUID. 16 | * @param [in] UUID The UUID to look up the service. 17 | * @return The characteristic. 18 | */ 19 | BLEService* BLEServiceMap::getByUUID(const char* uuid) { 20 | return getByUUID(BLEUUID(uuid)); 21 | } 22 | 23 | /** 24 | * @brief Return the service by UUID. 25 | * @param [in] UUID The UUID to look up the service. 26 | * @return The characteristic. 27 | */ 28 | BLEService* BLEServiceMap::getByUUID(BLEUUID uuid, uint8_t inst_id) { 29 | for (auto &myPair : m_uuidMap) { 30 | if (myPair.first->getUUID().equals(uuid)) { 31 | return myPair.first; 32 | } 33 | } 34 | //return m_uuidMap.at(uuid.toString()); 35 | return nullptr; 36 | } // getByUUID 37 | 38 | 39 | /** 40 | * @brief Return the service by handle. 41 | * @param [in] handle The handle to look up the service. 42 | * @return The service. 43 | */ 44 | BLEService* BLEServiceMap::getByHandle(uint16_t handle) { 45 | return m_handleMap.at(handle); 46 | } // getByHandle 47 | 48 | 49 | /** 50 | * @brief Set the service by UUID. 51 | * @param [in] uuid The uuid of the service. 52 | * @param [in] characteristic The service to cache. 53 | * @return N/A. 54 | */ 55 | void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService* service) { 56 | m_uuidMap.insert(std::pair(service, uuid.toString())); 57 | } // setByUUID 58 | 59 | 60 | /** 61 | * @brief Set the service by handle. 62 | * @param [in] handle The handle of the service. 63 | * @param [in] service The service to cache. 64 | * @return N/A. 65 | */ 66 | void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { 67 | m_handleMap.insert(std::pair(handle, service)); 68 | } // setByHandle 69 | 70 | 71 | /** 72 | * @brief Return a string representation of the service map. 73 | * @return A string representation of the service map. 74 | */ 75 | std::string BLEServiceMap::toString() { 76 | std::stringstream stringStream; 77 | stringStream << std::hex << std::setfill('0'); 78 | for (auto &myPair: m_handleMap) { 79 | stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; 80 | } 81 | return stringStream.str(); 82 | } // toString 83 | 84 | void BLEServiceMap::handleGATTServerEvent( 85 | esp_gatts_cb_event_t event, 86 | esp_gatt_if_t gatts_if, 87 | esp_ble_gatts_cb_param_t* param) { 88 | // Invoke the handler for every Service we have. 89 | for (auto &myPair : m_uuidMap) { 90 | myPair.first->handleGATTServerEvent(event, gatts_if, param); 91 | } 92 | } 93 | 94 | /** 95 | * @brief Get the first service in the map. 96 | * @return The first service in the map. 97 | */ 98 | BLEService* BLEServiceMap::getFirst() { 99 | m_iterator = m_uuidMap.begin(); 100 | if (m_iterator == m_uuidMap.end()) return nullptr; 101 | BLEService* pRet = m_iterator->first; 102 | m_iterator++; 103 | return pRet; 104 | } // getFirst 105 | 106 | /** 107 | * @brief Get the next service in the map. 108 | * @return The next service in the map. 109 | */ 110 | BLEService* BLEServiceMap::getNext() { 111 | if (m_iterator == m_uuidMap.end()) return nullptr; 112 | BLEService* pRet = m_iterator->first; 113 | m_iterator++; 114 | return pRet; 115 | } // getNext 116 | 117 | /** 118 | * @brief Removes service from maps. 119 | * @return N/A. 120 | */ 121 | void BLEServiceMap::removeService(BLEService* service) { 122 | m_handleMap.erase(service->getHandle()); 123 | m_uuidMap.erase(service); 124 | } // removeService 125 | 126 | /** 127 | * @brief Returns the amount of registered services 128 | * @return amount of registered services 129 | */ 130 | int BLEServiceMap::getRegisteredServiceCount(){ 131 | return m_handleMap.size(); 132 | } 133 | 134 | #endif /* CONFIG_BT_ENABLED */ 135 | -------------------------------------------------------------------------------- /src/BLEUUID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEUUID.cpp 3 | * 4 | * Created on: Jun 21, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "BLEUUID.h" 16 | 17 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 18 | #include "esp32-hal-log.h" 19 | #define LOG_TAG "" 20 | #else 21 | #include "esp_log.h" 22 | static const char* LOG_TAG = "BLEUUID"; 23 | #endif 24 | 25 | 26 | /** 27 | * @brief Copy memory from source to target but in reverse order. 28 | * 29 | * When we move memory from one location it is normally: 30 | * 31 | * ``` 32 | * [0][1][2]...[n] -> [0][1][2]...[n] 33 | * ``` 34 | * 35 | * with this function, it is: 36 | * 37 | * ``` 38 | * [0][1][2]...[n] -> [n][n-1][n-2]...[0] 39 | * ``` 40 | * 41 | * @param [in] target The target of the copy 42 | * @param [in] source The source of the copy 43 | * @param [in] size The number of bytes to copy 44 | */ 45 | static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { 46 | assert(size > 0); 47 | target += (size - 1); // Point target to the last byte of the target data 48 | while (size > 0) { 49 | *target = *source; 50 | target--; 51 | source++; 52 | size--; 53 | } 54 | } // memrcpy 55 | 56 | 57 | /** 58 | * @brief Create a UUID from a string. 59 | * 60 | * Create a UUID from a string. There will be two possible stories here. Either the string represents 61 | * a binary data field or the string represents a hex encoding of a UUID. 62 | * For the hex encoding, here is an example: 63 | * 64 | * ``` 65 | * "beb5483e-36e1-4688-b7f5-ea07361b26a8" 66 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 67 | * 12345678-90ab-cdef-1234-567890abcdef 68 | * ``` 69 | * 70 | * This has a length of 36 characters. We need to parse this into 16 bytes. 71 | * 72 | * @param [in] value The string to build a UUID from. 73 | */ 74 | BLEUUID::BLEUUID(std::string value) { 75 | m_valueSet = true; 76 | if (value.length() == 4) { 77 | m_uuid.len = ESP_UUID_LEN_16; 78 | m_uuid.uuid.uuid16 = 0; 79 | for(int i=0;i '9') MSB -= 7; 84 | if(LSB > '9') LSB -= 7; 85 | m_uuid.uuid.uuid16 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4; 86 | i+=2; 87 | } 88 | } 89 | else if (value.length() == 8) { 90 | m_uuid.len = ESP_UUID_LEN_32; 91 | m_uuid.uuid.uuid32 = 0; 92 | for(int i=0;i '9') MSB -= 7; 97 | if(LSB > '9') LSB -= 7; 98 | m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; 99 | i+=2; 100 | } 101 | } 102 | else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) 103 | m_uuid.len = ESP_UUID_LEN_128; 104 | memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); 105 | } 106 | else if (value.length() == 36) { 107 | // If the length of the string is 36 bytes then we will assume it is a long hex string in 108 | // UUID format. 109 | m_uuid.len = ESP_UUID_LEN_128; 110 | int n = 0; 111 | for(int i=0;i '9') MSB -= 7; 118 | if(LSB > '9') LSB -= 7; 119 | m_uuid.uuid.uuid128[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F); 120 | i+=2; 121 | } 122 | } 123 | else { 124 | ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes"); 125 | m_valueSet = false; 126 | } 127 | } //BLEUUID(std::string) 128 | 129 | 130 | /** 131 | * @brief Create a UUID from 16 bytes of memory. 132 | * 133 | * @param [in] pData The pointer to the start of the UUID. 134 | * @param [in] size The size of the data. 135 | * @param [in] msbFirst Is the MSB first in pData memory? 136 | */ 137 | BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { 138 | if (size != 16) { 139 | ESP_LOGE(LOG_TAG, "ERROR: UUID length not 16 bytes"); 140 | return; 141 | } 142 | m_uuid.len = ESP_UUID_LEN_128; 143 | if (msbFirst) { 144 | memrcpy(m_uuid.uuid.uuid128, pData, 16); 145 | } else { 146 | memcpy(m_uuid.uuid.uuid128, pData, 16); 147 | } 148 | m_valueSet = true; 149 | } // BLEUUID 150 | 151 | 152 | /** 153 | * @brief Create a UUID from the 16bit value. 154 | * 155 | * @param [in] uuid The 16bit short form UUID. 156 | */ 157 | BLEUUID::BLEUUID(uint16_t uuid) { 158 | m_uuid.len = ESP_UUID_LEN_16; 159 | m_uuid.uuid.uuid16 = uuid; 160 | m_valueSet = true; 161 | } // BLEUUID 162 | 163 | 164 | /** 165 | * @brief Create a UUID from the 32bit value. 166 | * 167 | * @param [in] uuid The 32bit short form UUID. 168 | */ 169 | BLEUUID::BLEUUID(uint32_t uuid) { 170 | m_uuid.len = ESP_UUID_LEN_32; 171 | m_uuid.uuid.uuid32 = uuid; 172 | m_valueSet = true; 173 | } // BLEUUID 174 | 175 | 176 | /** 177 | * @brief Create a UUID from the native UUID. 178 | * 179 | * @param [in] uuid The native UUID. 180 | */ 181 | BLEUUID::BLEUUID(esp_bt_uuid_t uuid) { 182 | m_uuid = uuid; 183 | m_valueSet = true; 184 | } // BLEUUID 185 | 186 | 187 | /** 188 | * @brief Create a UUID from the ESP32 esp_gat_id_t. 189 | * 190 | * @param [in] gattId The data to create the UUID from. 191 | */ 192 | BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) { 193 | } // BLEUUID 194 | 195 | 196 | BLEUUID::BLEUUID() { 197 | m_valueSet = false; 198 | } // BLEUUID 199 | 200 | 201 | /** 202 | * @brief Get the number of bits in this uuid. 203 | * @return The number of bits in the UUID. One of 16, 32 or 128. 204 | */ 205 | uint8_t BLEUUID::bitSize() { 206 | if (!m_valueSet) return 0; 207 | switch (m_uuid.len) { 208 | case ESP_UUID_LEN_16: 209 | return 16; 210 | case ESP_UUID_LEN_32: 211 | return 32; 212 | case ESP_UUID_LEN_128: 213 | return 128; 214 | default: 215 | ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); 216 | return 0; 217 | } // End of switch 218 | } // bitSize 219 | 220 | 221 | /** 222 | * @brief Compare a UUID against this UUID. 223 | * 224 | * @param [in] uuid The UUID to compare against. 225 | * @return True if the UUIDs are equal and false otherwise. 226 | */ 227 | bool BLEUUID::equals(BLEUUID uuid) { 228 | //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str()); 229 | if (!m_valueSet || !uuid.m_valueSet) return false; 230 | 231 | if (uuid.m_uuid.len != m_uuid.len) { 232 | return uuid.toString() == toString(); 233 | } 234 | 235 | if (uuid.m_uuid.len == ESP_UUID_LEN_16) { 236 | return uuid.m_uuid.uuid.uuid16 == m_uuid.uuid.uuid16; 237 | } 238 | 239 | if (uuid.m_uuid.len == ESP_UUID_LEN_32) { 240 | return uuid.m_uuid.uuid.uuid32 == m_uuid.uuid.uuid32; 241 | } 242 | 243 | return memcmp(uuid.m_uuid.uuid.uuid128, m_uuid.uuid.uuid128, 16) == 0; 244 | } // equals 245 | 246 | 247 | /** 248 | * Create a BLEUUID from a string of the form: 249 | * 0xNNNN 250 | * 0xNNNNNNNN 251 | * 0x 252 | * NNNN 253 | * NNNNNNNN 254 | * 255 | */ 256 | BLEUUID BLEUUID::fromString(std::string _uuid) { 257 | uint8_t start = 0; 258 | if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. 259 | start = 2; 260 | } 261 | uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. 262 | 263 | if(len == 4) { 264 | uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); 265 | return BLEUUID(x); 266 | } else if (len == 8) { 267 | uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); 268 | return BLEUUID(x); 269 | } else if (len == 36) { 270 | return BLEUUID(_uuid); 271 | } 272 | return BLEUUID(); 273 | } // fromString 274 | 275 | 276 | /** 277 | * @brief Get the native UUID value. 278 | * 279 | * @return The native UUID value or NULL if not set. 280 | */ 281 | esp_bt_uuid_t* BLEUUID::getNative() { 282 | //ESP_LOGD(TAG, ">> getNative()") 283 | if (m_valueSet == false) { 284 | ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!"); 285 | return nullptr; 286 | } 287 | //ESP_LOGD(TAG, "<< getNative()"); 288 | return &m_uuid; 289 | } // getNative 290 | 291 | 292 | /** 293 | * @brief Convert a UUID to its 128 bit representation. 294 | * 295 | * A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method 296 | * will convert 16 or 32 bit representations to the full 128bit. 297 | */ 298 | BLEUUID BLEUUID::to128() { 299 | //ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str()); 300 | 301 | // If we either don't have a value or are already a 128 bit UUID, nothing further to do. 302 | if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) { 303 | return *this; 304 | } 305 | 306 | // If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID. 307 | if (m_uuid.len == ESP_UUID_LEN_16) { 308 | uint16_t temp = m_uuid.uuid.uuid16; 309 | m_uuid.uuid.uuid128[15] = 0; 310 | m_uuid.uuid.uuid128[14] = 0; 311 | m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff; 312 | m_uuid.uuid.uuid128[12] = temp & 0xff; 313 | 314 | } 315 | else if (m_uuid.len == ESP_UUID_LEN_32) { 316 | uint32_t temp = m_uuid.uuid.uuid32; 317 | m_uuid.uuid.uuid128[15] = (temp >> 24) & 0xff; 318 | m_uuid.uuid.uuid128[14] = (temp >> 16) & 0xff; 319 | m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff; 320 | m_uuid.uuid.uuid128[12] = temp & 0xff; 321 | } 322 | 323 | // Set the fixed parts of the UUID. 324 | m_uuid.uuid.uuid128[11] = 0x00; 325 | m_uuid.uuid.uuid128[10] = 0x00; 326 | 327 | m_uuid.uuid.uuid128[9] = 0x10; 328 | m_uuid.uuid.uuid128[8] = 0x00; 329 | 330 | m_uuid.uuid.uuid128[7] = 0x80; 331 | m_uuid.uuid.uuid128[6] = 0x00; 332 | 333 | m_uuid.uuid.uuid128[5] = 0x00; 334 | m_uuid.uuid.uuid128[4] = 0x80; 335 | m_uuid.uuid.uuid128[3] = 0x5f; 336 | m_uuid.uuid.uuid128[2] = 0x9b; 337 | m_uuid.uuid.uuid128[1] = 0x34; 338 | m_uuid.uuid.uuid128[0] = 0xfb; 339 | 340 | m_uuid.len = ESP_UUID_LEN_128; 341 | //ESP_LOGD(TAG, "<< toFull <- %s", toString().c_str()); 342 | return *this; 343 | } // to128 344 | 345 | 346 | 347 | 348 | /** 349 | * @brief Get a string representation of the UUID. 350 | * 351 | * The format of a string is: 352 | * 01234567 8901 2345 6789 012345678901 353 | * 0000180d-0000-1000-8000-00805f9b34fb 354 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 355 | * 356 | * @return A string representation of the UUID. 357 | */ 358 | std::string BLEUUID::toString() { 359 | if (!m_valueSet) return ""; // If we have no value, nothing to format. 360 | 361 | // If the UUIDs are 16 or 32 bit, pad correctly. 362 | std::stringstream ss; 363 | 364 | if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. 365 | ss << "0000" << 366 | std::hex << 367 | std::setfill('0') << 368 | std::setw(4) << 369 | m_uuid.uuid.uuid16 << 370 | "-0000-1000-8000-00805f9b34fb"; 371 | return ss.str(); // Return the string 372 | } // End 16bit UUID 373 | 374 | if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. 375 | ss << std::hex << 376 | std::setfill('0') << 377 | std::setw(8) << 378 | m_uuid.uuid.uuid32 << 379 | "-0000-1000-8000-00805f9b34fb"; 380 | return ss.str(); // return the string 381 | } // End 32bit UUID 382 | 383 | // The UUID is not 16bit or 32bit which means that it is 128bit. 384 | // 385 | // UUID string format: 386 | // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 387 | ss << std::hex << std::setfill('0') << 388 | std::setw(2) << (int) m_uuid.uuid.uuid128[15] << 389 | std::setw(2) << (int) m_uuid.uuid.uuid128[14] << 390 | std::setw(2) << (int) m_uuid.uuid.uuid128[13] << 391 | std::setw(2) << (int) m_uuid.uuid.uuid128[12] << "-" << 392 | std::setw(2) << (int) m_uuid.uuid.uuid128[11] << 393 | std::setw(2) << (int) m_uuid.uuid.uuid128[10] << "-" << 394 | std::setw(2) << (int) m_uuid.uuid.uuid128[9] << 395 | std::setw(2) << (int) m_uuid.uuid.uuid128[8] << "-" << 396 | std::setw(2) << (int) m_uuid.uuid.uuid128[7] << 397 | std::setw(2) << (int) m_uuid.uuid.uuid128[6] << "-" << 398 | std::setw(2) << (int) m_uuid.uuid.uuid128[5] << 399 | std::setw(2) << (int) m_uuid.uuid.uuid128[4] << 400 | std::setw(2) << (int) m_uuid.uuid.uuid128[3] << 401 | std::setw(2) << (int) m_uuid.uuid.uuid128[2] << 402 | std::setw(2) << (int) m_uuid.uuid.uuid128[1] << 403 | std::setw(2) << (int) m_uuid.uuid.uuid128[0]; 404 | return ss.str(); 405 | } // toString 406 | 407 | #endif /* CONFIG_BT_ENABLED */ 408 | -------------------------------------------------------------------------------- /src/BLEUUID.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEUUID.h 3 | * 4 | * Created on: Jun 21, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEUUID_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | #include 14 | 15 | /** 16 | * @brief A model of a %BLE UUID. 17 | */ 18 | class BLEUUID { 19 | public: 20 | BLEUUID(std::string uuid); 21 | BLEUUID(uint16_t uuid); 22 | BLEUUID(uint32_t uuid); 23 | BLEUUID(esp_bt_uuid_t uuid); 24 | BLEUUID(uint8_t* pData, size_t size, bool msbFirst); 25 | BLEUUID(esp_gatt_id_t gattId); 26 | BLEUUID(); 27 | uint8_t bitSize(); // Get the number of bits in this uuid. 28 | bool equals(BLEUUID uuid); 29 | esp_bt_uuid_t* getNative(); 30 | BLEUUID to128(); 31 | std::string toString(); 32 | static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string 33 | 34 | private: 35 | esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. 36 | bool m_valueSet = false; // Is there a value set for this instance. 37 | }; // BLEUUID 38 | #endif /* CONFIG_BT_ENABLED */ 39 | #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ 40 | -------------------------------------------------------------------------------- /src/BLEUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEUtils.h 3 | * 4 | * Created on: Mar 25, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEUTILS_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include // ESP32 BLE 13 | #include // ESP32 BLE 14 | #include // ESP32 BLE 15 | #include 16 | #include "BLEClient.h" 17 | 18 | /** 19 | * @brief A set of general %BLE utilities. 20 | */ 21 | class BLEUtils { 22 | public: 23 | static const char* addressTypeToString(esp_ble_addr_type_t type); 24 | static std::string adFlagsToString(uint8_t adFlags); 25 | static const char* advTypeToString(uint8_t advType); 26 | static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); 27 | static std::string buildPrintData(uint8_t* source, size_t length); 28 | static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); 29 | static const char* devTypeToString(esp_bt_dev_type_t type); 30 | static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id = 0); 31 | static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true); 32 | static void dumpGapEvent( 33 | esp_gap_ble_cb_event_t event, 34 | esp_ble_gap_cb_param_t* param); 35 | static void dumpGattClientEvent( 36 | esp_gattc_cb_event_t event, 37 | esp_gatt_if_t gattc_if, 38 | esp_ble_gattc_cb_param_t* evtParam); 39 | static void dumpGattServerEvent( 40 | esp_gatts_cb_event_t event, 41 | esp_gatt_if_t gatts_if, 42 | esp_ble_gatts_cb_param_t* evtParam); 43 | static const char* eventTypeToString(esp_ble_evt_type_t eventType); 44 | static BLEClient* findByAddress(BLEAddress address); 45 | static BLEClient* findByConnId(uint16_t conn_id); 46 | static const char* gapEventToString(uint32_t eventType); 47 | static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); 48 | static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); 49 | static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); 50 | static std::string gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement); 51 | static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); 52 | static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); 53 | static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); 54 | static std::string gattServiceToString(uint32_t serviceId); 55 | static std::string gattStatusToString(esp_gatt_status_t status); 56 | static std::string getMember(uint32_t memberId); 57 | static void registerByAddress(BLEAddress address, BLEClient* pDevice); 58 | static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); 59 | static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt); 60 | }; 61 | 62 | #endif // CONFIG_BT_ENABLED 63 | #endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */ 64 | -------------------------------------------------------------------------------- /src/BLEValue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEValue.cpp 3 | * 4 | * Created on: Jul 17, 2017 5 | * Author: kolban 6 | */ 7 | #include "sdkconfig.h" 8 | #if defined(CONFIG_BT_ENABLED) 9 | #include "BLEValue.h" 10 | 11 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 12 | #include "esp32-hal-log.h" 13 | #define LOG_TAG "" 14 | #else 15 | #include "esp_log.h" 16 | static const char* LOG_TAG="BLEValue"; 17 | #endif 18 | 19 | 20 | 21 | BLEValue::BLEValue() { 22 | m_accumulation = ""; 23 | m_value = ""; 24 | m_readOffset = 0; 25 | } // BLEValue 26 | 27 | 28 | /** 29 | * @brief Add a message part to the accumulation. 30 | * The accumulation is a growing set of data that is added to until a commit or cancel. 31 | * @param [in] part A message part being added. 32 | */ 33 | void BLEValue::addPart(std::string part) { 34 | ESP_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); 35 | m_accumulation += part; 36 | } // addPart 37 | 38 | 39 | /** 40 | * @brief Add a message part to the accumulation. 41 | * The accumulation is a growing set of data that is added to until a commit or cancel. 42 | * @param [in] pData A message part being added. 43 | * @param [in] length The number of bytes being added. 44 | */ 45 | void BLEValue::addPart(uint8_t* pData, size_t length) { 46 | ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length); 47 | m_accumulation += std::string((char*) pData, length); 48 | } // addPart 49 | 50 | 51 | /** 52 | * @brief Cancel the current accumulation. 53 | */ 54 | void BLEValue::cancel() { 55 | ESP_LOGD(LOG_TAG, ">> cancel"); 56 | m_accumulation = ""; 57 | m_readOffset = 0; 58 | } // cancel 59 | 60 | 61 | /** 62 | * @brief Commit the current accumulation. 63 | * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces 64 | * of the overall message. After the last part has been received, we may perform a commit which means that 65 | * we now have the complete message and commit the change as a unit. 66 | */ 67 | void BLEValue::commit() { 68 | ESP_LOGD(LOG_TAG, ">> commit"); 69 | // If there is nothing to commit, do nothing. 70 | if (m_accumulation.length() == 0) return; 71 | setValue(m_accumulation); 72 | m_accumulation = ""; 73 | m_readOffset = 0; 74 | } // commit 75 | 76 | 77 | /** 78 | * @brief Get a pointer to the data. 79 | * @return A pointer to the data. 80 | */ 81 | uint8_t* BLEValue::getData() { 82 | return (uint8_t*) m_value.data(); 83 | } 84 | 85 | 86 | /** 87 | * @brief Get the length of the data in bytes. 88 | * @return The length of the data in bytes. 89 | */ 90 | size_t BLEValue::getLength() { 91 | return m_value.length(); 92 | } // getLength 93 | 94 | 95 | /** 96 | * @brief Get the read offset. 97 | * @return The read offset into the read. 98 | */ 99 | uint16_t BLEValue::getReadOffset() { 100 | return m_readOffset; 101 | } // getReadOffset 102 | 103 | 104 | /** 105 | * @brief Get the current value. 106 | */ 107 | std::string BLEValue::getValue() { 108 | return m_value; 109 | } // getValue 110 | 111 | 112 | /** 113 | * @brief Set the read offset 114 | * @param [in] readOffset The offset into the read. 115 | */ 116 | void BLEValue::setReadOffset(uint16_t readOffset) { 117 | m_readOffset = readOffset; 118 | } // setReadOffset 119 | 120 | 121 | /** 122 | * @brief Set the current value. 123 | */ 124 | void BLEValue::setValue(std::string value) { 125 | m_value = value; 126 | } // setValue 127 | 128 | 129 | /** 130 | * @brief Set the current value. 131 | * @param [in] pData The data for the current value. 132 | * @param [in] The length of the new current value. 133 | */ 134 | void BLEValue::setValue(uint8_t* pData, size_t length) { 135 | m_value = std::string((char*) pData, length); 136 | } // setValue 137 | 138 | 139 | #endif // CONFIG_BT_ENABLED 140 | -------------------------------------------------------------------------------- /src/BLEValue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEValue.h 3 | * 4 | * Created on: Jul 17, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_ 9 | #define COMPONENTS_CPP_UTILS_BLEVALUE_H_ 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | 14 | /** 15 | * @brief The model of a %BLE value. 16 | */ 17 | class BLEValue { 18 | public: 19 | BLEValue(); 20 | void addPart(std::string part); 21 | void addPart(uint8_t* pData, size_t length); 22 | void cancel(); 23 | void commit(); 24 | uint8_t* getData(); 25 | size_t getLength(); 26 | uint16_t getReadOffset(); 27 | std::string getValue(); 28 | void setReadOffset(uint16_t readOffset); 29 | void setValue(std::string value); 30 | void setValue(uint8_t* pData, size_t length); 31 | 32 | private: 33 | std::string m_accumulation; 34 | uint16_t m_readOffset; 35 | std::string m_value; 36 | 37 | }; 38 | #endif // CONFIG_BT_ENABLED 39 | #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ 40 | -------------------------------------------------------------------------------- /src/FreeRTOS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS.cpp 3 | * 4 | * Created on: Feb 24, 2017 5 | * Author: kolban 6 | */ 7 | #include // Include the base FreeRTOS definitions 8 | #include // Include the task definitions 9 | #include // Include the semaphore definitions 10 | #include 11 | #include 12 | #include 13 | #include "FreeRTOS.h" 14 | #include "sdkconfig.h" 15 | #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) 16 | #include "esp32-hal-log.h" 17 | #define LOG_TAG "" 18 | #else 19 | #include "esp_log.h" 20 | static const char* LOG_TAG = "FreeRTOS"; 21 | #endif 22 | 23 | 24 | /** 25 | * Sleep for the specified number of milliseconds. 26 | * @param[in] ms The period in milliseconds for which to sleep. 27 | */ 28 | void FreeRTOS::sleep(uint32_t ms) { 29 | ::vTaskDelay(ms / portTICK_PERIOD_MS); 30 | } // sleep 31 | 32 | 33 | /** 34 | * Start a new task. 35 | * @param[in] task The function pointer to the function to be run in the task. 36 | * @param[in] taskName A string identifier for the task. 37 | * @param[in] param An optional parameter to be passed to the started task. 38 | * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. 39 | */ 40 | void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) { 41 | ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); 42 | } // startTask 43 | 44 | 45 | /** 46 | * Delete the task. 47 | * @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted. 48 | */ 49 | void FreeRTOS::deleteTask(TaskHandle_t pTask) { 50 | ::vTaskDelete(pTask); 51 | } // deleteTask 52 | 53 | 54 | /** 55 | * Get the time in milliseconds since the %FreeRTOS scheduler started. 56 | * @return The time in milliseconds since the %FreeRTOS scheduler started. 57 | */ 58 | uint32_t FreeRTOS::getTimeSinceStart() { 59 | return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS); 60 | } // getTimeSinceStart 61 | 62 | 63 | /** 64 | * @brief Wait for a semaphore to be released by trying to take it and 65 | * then releasing it again. 66 | * @param [in] owner A debug tag. 67 | * @return The value associated with the semaphore. 68 | */ 69 | uint32_t FreeRTOS::Semaphore::wait(std::string owner) { 70 | ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); 71 | 72 | if (m_usePthreads) { 73 | pthread_mutex_lock(&m_pthread_mutex); 74 | } else { 75 | xSemaphoreTake(m_semaphore, portMAX_DELAY); 76 | } 77 | 78 | m_owner = owner; 79 | 80 | if (m_usePthreads) { 81 | pthread_mutex_unlock(&m_pthread_mutex); 82 | } else { 83 | xSemaphoreGive(m_semaphore); 84 | } 85 | 86 | ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); 87 | m_owner = std::string(""); 88 | return m_value; 89 | } // wait 90 | 91 | 92 | FreeRTOS::Semaphore::Semaphore(std::string name) { 93 | m_usePthreads = false; // Are we using pThreads or FreeRTOS? 94 | if (m_usePthreads) { 95 | pthread_mutex_init(&m_pthread_mutex, nullptr); 96 | } else { 97 | m_semaphore = xSemaphoreCreateMutex(); 98 | } 99 | 100 | m_name = name; 101 | m_owner = std::string(""); 102 | m_value = 0; 103 | } 104 | 105 | 106 | FreeRTOS::Semaphore::~Semaphore() { 107 | if (m_usePthreads) { 108 | pthread_mutex_destroy(&m_pthread_mutex); 109 | } else { 110 | vSemaphoreDelete(m_semaphore); 111 | } 112 | } 113 | 114 | 115 | /** 116 | * @brief Give a semaphore. 117 | * The Semaphore is given. 118 | */ 119 | void FreeRTOS::Semaphore::give() { 120 | ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); 121 | if (m_usePthreads) { 122 | pthread_mutex_unlock(&m_pthread_mutex); 123 | } else { 124 | xSemaphoreGive(m_semaphore); 125 | } 126 | // #ifdef ARDUINO_ARCH_ESP32 127 | // FreeRTOS::sleep(10); 128 | // #endif 129 | 130 | m_owner = std::string(""); 131 | } // Semaphore::give 132 | 133 | 134 | /** 135 | * @brief Give a semaphore. 136 | * The Semaphore is given with an associated value. 137 | * @param [in] value The value to associate with the semaphore. 138 | */ 139 | void FreeRTOS::Semaphore::give(uint32_t value) { 140 | m_value = value; 141 | give(); 142 | } // give 143 | 144 | 145 | /** 146 | * @brief Give a semaphore from an ISR. 147 | */ 148 | void FreeRTOS::Semaphore::giveFromISR() { 149 | BaseType_t higherPriorityTaskWoken; 150 | if (m_usePthreads) { 151 | assert(false); 152 | } else { 153 | xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); 154 | } 155 | } // giveFromISR 156 | 157 | 158 | /** 159 | * @brief Take a semaphore. 160 | * Take a semaphore and wait indefinitely. 161 | * @param [in] owner The new owner (for debugging) 162 | * @return True if we took the semaphore. 163 | */ 164 | bool FreeRTOS::Semaphore::take(std::string owner) { 165 | ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); 166 | bool rc = false; 167 | if (m_usePthreads) { 168 | pthread_mutex_lock(&m_pthread_mutex); 169 | } else { 170 | rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; 171 | } 172 | m_owner = owner; 173 | if (rc) { 174 | ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); 175 | } else { 176 | ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); 177 | } 178 | return rc; 179 | } // Semaphore::take 180 | 181 | 182 | /** 183 | * @brief Take a semaphore. 184 | * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. 185 | * @param [in] timeoutMs Timeout in milliseconds. 186 | * @param [in] owner The new owner (for debugging) 187 | * @return True if we took the semaphore. 188 | */ 189 | bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { 190 | ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); 191 | bool rc = false; 192 | if (m_usePthreads) { 193 | assert(false); // We apparently don't have a timed wait for pthreads. 194 | } else { 195 | rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; 196 | } 197 | m_owner = owner; 198 | if (rc) { 199 | ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); 200 | } else { 201 | ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); 202 | } 203 | return rc; 204 | } // Semaphore::take 205 | 206 | 207 | 208 | /** 209 | * @brief Create a string representation of the semaphore. 210 | * @return A string representation of the semaphore. 211 | */ 212 | std::string FreeRTOS::Semaphore::toString() { 213 | std::stringstream stringStream; 214 | stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; 215 | return stringStream.str(); 216 | } // toString 217 | 218 | 219 | /** 220 | * @brief Set the name of the semaphore. 221 | * @param [in] name The name of the semaphore. 222 | */ 223 | void FreeRTOS::Semaphore::setName(std::string name) { 224 | m_name = name; 225 | } // setName 226 | 227 | 228 | /** 229 | * @brief Create a ring buffer. 230 | * @param [in] length The amount of storage to allocate for the ring buffer. 231 | * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF. 232 | */ 233 | Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) { 234 | m_handle = ::xRingbufferCreate(length, type); 235 | } // Ringbuffer 236 | 237 | 238 | Ringbuffer::~Ringbuffer() { 239 | ::vRingbufferDelete(m_handle); 240 | } // ~Ringbuffer 241 | 242 | 243 | /** 244 | * @brief Receive data from the buffer. 245 | * @param [out] size On return, the size of data returned. 246 | * @param [in] wait How long to wait. 247 | * @return A pointer to the storage retrieved. 248 | */ 249 | void* Ringbuffer::receive(size_t* size, TickType_t wait) { 250 | return ::xRingbufferReceive(m_handle, size, wait); 251 | } // receive 252 | 253 | 254 | /** 255 | * @brief Return an item. 256 | * @param [in] item The item to be returned/released. 257 | */ 258 | void Ringbuffer::returnItem(void* item) { 259 | ::vRingbufferReturnItem(m_handle, item); 260 | } // returnItem 261 | 262 | 263 | /** 264 | * @brief Send data to the buffer. 265 | * @param [in] data The data to place into the buffer. 266 | * @param [in] length The length of data to place into the buffer. 267 | * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. 268 | * @return 269 | */ 270 | bool Ringbuffer::send(void* data, size_t length, TickType_t wait) { 271 | return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE; 272 | } // send 273 | 274 | 275 | -------------------------------------------------------------------------------- /src/FreeRTOS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS.h 3 | * 4 | * Created on: Feb 24, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef MAIN_FREERTOS_H_ 9 | #define MAIN_FREERTOS_H_ 10 | #include 11 | #include 12 | #include 13 | 14 | #include // Include the base FreeRTOS definitions. 15 | #include // Include the task definitions. 16 | #include // Include the semaphore definitions. 17 | #include // Include the ringbuffer definitions. 18 | 19 | 20 | /** 21 | * @brief Interface to %FreeRTOS functions. 22 | */ 23 | class FreeRTOS { 24 | public: 25 | static void sleep(uint32_t ms); 26 | static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048); 27 | static void deleteTask(TaskHandle_t pTask = nullptr); 28 | 29 | static uint32_t getTimeSinceStart(); 30 | 31 | class Semaphore { 32 | public: 33 | Semaphore(std::string owner = ""); 34 | ~Semaphore(); 35 | void give(); 36 | void give(uint32_t value); 37 | void giveFromISR(); 38 | void setName(std::string name); 39 | bool take(std::string owner = ""); 40 | bool take(uint32_t timeoutMs, std::string owner = ""); 41 | std::string toString(); 42 | uint32_t wait(std::string owner = ""); 43 | 44 | private: 45 | SemaphoreHandle_t m_semaphore; 46 | pthread_mutex_t m_pthread_mutex; 47 | std::string m_name; 48 | std::string m_owner; 49 | uint32_t m_value; 50 | bool m_usePthreads; 51 | 52 | }; 53 | }; 54 | 55 | 56 | /** 57 | * @brief Ringbuffer. 58 | */ 59 | class Ringbuffer { 60 | public: 61 | Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT); 62 | ~Ringbuffer(); 63 | 64 | void* receive(size_t* size, TickType_t wait = portMAX_DELAY); 65 | void returnItem(void* item); 66 | bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY); 67 | private: 68 | RingbufHandle_t m_handle; 69 | }; 70 | 71 | #endif /* MAIN_FREERTOS_H_ */ 72 | -------------------------------------------------------------------------------- /src/GeneralUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GeneralUtils.h 3 | * 4 | * Created on: May 20, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #ifndef COMPONENTS_CPP_UTILS_GENERALUTILS_H_ 9 | #define COMPONENTS_CPP_UTILS_GENERALUTILS_H_ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * @brief General utilities. 18 | */ 19 | class GeneralUtils { 20 | public: 21 | static bool base64Decode(const std::string& in, std::string* out); 22 | static bool base64Encode(const std::string& in, std::string* out); 23 | static void dumpInfo(); 24 | static bool endsWith(std::string str, char c); 25 | static const char* errorToString(esp_err_t errCode); 26 | static const char* wifiErrorToString(uint8_t value); 27 | static void hexDump(const uint8_t* pData, uint32_t length); 28 | static std::string ipToString(uint8_t* ip); 29 | static std::vector split(std::string source, char delimiter); 30 | static std::string toLower(std::string& value); 31 | static std::string trim(const std::string& str); 32 | 33 | }; 34 | 35 | #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ 36 | -------------------------------------------------------------------------------- /src/HIDTypes.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2011 mbed.org, MIT License 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | * and associated documentation files (the "Software"), to deal in the Software without 5 | * restriction, including without limitation the rights to use, copy, modify, merge, publish, 6 | * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 7 | * Software is furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or 10 | * substantial portions of the Software. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 15 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | #ifndef USBCLASS_HID_TYPES 20 | #define USBCLASS_HID_TYPES 21 | 22 | #include 23 | 24 | /* */ 25 | #define HID_VERSION_1_11 (0x0111) 26 | 27 | /* HID Class */ 28 | #define HID_CLASS (3) 29 | #define HID_SUBCLASS_NONE (0) 30 | #define HID_PROTOCOL_NONE (0) 31 | 32 | /* Descriptors */ 33 | #define HID_DESCRIPTOR (33) 34 | #define HID_DESCRIPTOR_LENGTH (0x09) 35 | #define REPORT_DESCRIPTOR (34) 36 | 37 | /* Class requests */ 38 | #define GET_REPORT (0x1) 39 | #define GET_IDLE (0x2) 40 | #define SET_REPORT (0x9) 41 | #define SET_IDLE (0xa) 42 | 43 | /* HID Class Report Descriptor */ 44 | /* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ 45 | /* of data as per HID Class standard */ 46 | 47 | /* Main items */ 48 | #ifdef ARDUINO_ARCH_ESP32 49 | #define HIDINPUT(size) (0x80 | size) 50 | #define HIDOUTPUT(size) (0x90 | size) 51 | #else 52 | #define INPUT(size) (0x80 | size) 53 | #define OUTPUT(size) (0x90 | size) 54 | #endif 55 | #define FEATURE(size) (0xb0 | size) 56 | #define COLLECTION(size) (0xa0 | size) 57 | #define END_COLLECTION(size) (0xc0 | size) 58 | 59 | /* Global items */ 60 | #define USAGE_PAGE(size) (0x04 | size) 61 | #define LOGICAL_MINIMUM(size) (0x14 | size) 62 | #define LOGICAL_MAXIMUM(size) (0x24 | size) 63 | #define PHYSICAL_MINIMUM(size) (0x34 | size) 64 | #define PHYSICAL_MAXIMUM(size) (0x44 | size) 65 | #define UNIT_EXPONENT(size) (0x54 | size) 66 | #define UNIT(size) (0x64 | size) 67 | #define REPORT_SIZE(size) (0x74 | size) //bits 68 | #define REPORT_ID(size) (0x84 | size) 69 | #define REPORT_COUNT(size) (0x94 | size) //bytes 70 | #define PUSH(size) (0xa4 | size) 71 | #define POP(size) (0xb4 | size) 72 | 73 | /* Local items */ 74 | #define USAGE(size) (0x08 | size) 75 | #define USAGE_MINIMUM(size) (0x18 | size) 76 | #define USAGE_MAXIMUM(size) (0x28 | size) 77 | #define DESIGNATOR_INDEX(size) (0x38 | size) 78 | #define DESIGNATOR_MINIMUM(size) (0x48 | size) 79 | #define DESIGNATOR_MAXIMUM(size) (0x58 | size) 80 | #define STRING_INDEX(size) (0x78 | size) 81 | #define STRING_MINIMUM(size) (0x88 | size) 82 | #define STRING_MAXIMUM(size) (0x98 | size) 83 | #define DELIMITER(size) (0xa8 | size) 84 | 85 | /* HID Report */ 86 | /* Where report IDs are used the first byte of 'data' will be the */ 87 | /* report ID and 'length' will include this report ID byte. */ 88 | 89 | #define MAX_HID_REPORT_SIZE (64) 90 | 91 | typedef struct { 92 | uint32_t length; 93 | uint8_t data[MAX_HID_REPORT_SIZE]; 94 | } HID_REPORT; 95 | 96 | #endif 97 | --------------------------------------------------------------------------------