├── 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_uart │ └── BLE_uart.ino └── BLE_write │ └── BLE_write.ino ├── library.properties └── src ├── BLE2902.cpp ├── BLE2902.h ├── BLEAdvertising.cpp ├── BLEAdvertising.h ├── BLECharacteristic.cpp ├── BLECharacteristic.h ├── BLECharacteristicMap.cpp ├── BLEDescriptor.cpp ├── BLEDescriptor.h ├── BLEDescriptorMap.cpp ├── BLEDevice.cpp ├── BLEDevice.h ├── BLEServer.cpp ├── BLEServer.h ├── BLEService.cpp ├── BLEService.h ├── BLEServiceMap.cpp ├── BLEUUID.cpp ├── BLEUUID.h ├── BLEValue.cpp ├── BLEValue.h ├── FreeRTOS.cpp ├── FreeRTOS.h ├── GeneralUtils.cpp ├── GeneralUtils.h ├── HIDKeyboardTypes.h └── HIDTypes.h /README.md: -------------------------------------------------------------------------------- 1 | # Light version of ESP32 BLE 2 | 3 | This is a fork of the official esp32 ble library and is stripped to decrease the size. It is only possible to use the ble server features. 4 | 5 | I saved about ~30KB program size with this version. 6 | 7 | The repository of the original library is here: https://github.com/nkolban/ESP32_BLE_Arduino 8 | 9 | -------------------------------------------------------------------------------- /examples/BLE_client/BLE_client.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * A BLE client example that is rich in capabilities. 3 | */ 4 | 5 | #include "BLEDevice.h" 6 | //#include "BLEScan.h" 7 | 8 | // The remote service we wish to connect to. 9 | static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); 10 | // The characteristic of the remote service we are interested in. 11 | static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); 12 | 13 | static BLEAddress *pServerAddress; 14 | static boolean doConnect = false; 15 | static boolean connected = false; 16 | static BLERemoteCharacteristic* pRemoteCharacteristic; 17 | 18 | static void notifyCallback( 19 | BLERemoteCharacteristic* pBLERemoteCharacteristic, 20 | uint8_t* pData, 21 | size_t length, 22 | bool isNotify) { 23 | Serial.print("Notify callback for characteristic "); 24 | Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); 25 | Serial.print(" of data length "); 26 | Serial.println(length); 27 | } 28 | 29 | bool connectToServer(BLEAddress pAddress) { 30 | Serial.print("Forming a connection to "); 31 | Serial.println(pAddress.toString().c_str()); 32 | 33 | BLEClient* pClient = BLEDevice::createClient(); 34 | Serial.println(" - Created client"); 35 | 36 | // Connect to the remove BLE Server. 37 | pClient->connect(pAddress); 38 | Serial.println(" - Connected to server"); 39 | 40 | // Obtain a reference to the service we are after in the remote BLE server. 41 | BLERemoteService* pRemoteService = pClient->getService(serviceUUID); 42 | if (pRemoteService == nullptr) { 43 | Serial.print("Failed to find our service UUID: "); 44 | Serial.println(serviceUUID.toString().c_str()); 45 | return false; 46 | } 47 | Serial.println(" - Found our service"); 48 | 49 | 50 | // Obtain a reference to the characteristic in the service of the remote BLE server. 51 | pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); 52 | if (pRemoteCharacteristic == nullptr) { 53 | Serial.print("Failed to find our characteristic UUID: "); 54 | Serial.println(charUUID.toString().c_str()); 55 | return false; 56 | } 57 | Serial.println(" - Found our characteristic"); 58 | 59 | // Read the value of the characteristic. 60 | std::string value = pRemoteCharacteristic->readValue(); 61 | Serial.print("The characteristic value was: "); 62 | Serial.println(value.c_str()); 63 | 64 | pRemoteCharacteristic->registerForNotify(notifyCallback); 65 | } 66 | /** 67 | * Scan for BLE servers and find the first one that advertises the service we are looking for. 68 | */ 69 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 70 | /** 71 | * Called for each advertising BLE server. 72 | */ 73 | void onResult(BLEAdvertisedDevice advertisedDevice) { 74 | Serial.print("BLE Advertised Device found: "); 75 | Serial.println(advertisedDevice.toString().c_str()); 76 | 77 | // We have found a device, let us now see if it contains the service we are looking for. 78 | if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { 79 | 80 | // 81 | Serial.print("Found our device! address: "); 82 | advertisedDevice.getScan()->stop(); 83 | 84 | pServerAddress = new BLEAddress(advertisedDevice.getAddress()); 85 | doConnect = true; 86 | 87 | } // Found our server 88 | } // onResult 89 | }; // MyAdvertisedDeviceCallbacks 90 | 91 | 92 | void setup() { 93 | Serial.begin(115200); 94 | Serial.println("Starting Arduino BLE Client application..."); 95 | BLEDevice::init(""); 96 | 97 | // Retrieve a Scanner and set the callback we want to use to be informed when we 98 | // have detected a new device. Specify that we want active scanning and start the 99 | // scan to run for 30 seconds. 100 | BLEScan* pBLEScan = BLEDevice::getScan(); 101 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 102 | pBLEScan->setActiveScan(true); 103 | pBLEScan->start(30); 104 | } // End of setup. 105 | 106 | 107 | // This is the Arduino main loop function. 108 | void loop() { 109 | 110 | // If the flag "doConnect" is true then we have scanned for and found the desired 111 | // BLE Server with which we wish to connect. Now we connect to it. Once we are 112 | // connected we set the connected flag to be true. 113 | if (doConnect == true) { 114 | if (connectToServer(*pServerAddress)) { 115 | Serial.println("We are now connected to the BLE Server."); 116 | connected = true; 117 | } else { 118 | Serial.println("We have failed to connect to the server; there is nothin more we will do."); 119 | } 120 | doConnect = false; 121 | } 122 | 123 | // If we are connected to a peer BLE Server, update the characteristic each time we are reached 124 | // with the current time since boot. 125 | if (connected) { 126 | String newValue = "Time since boot: " + String(millis()/1000); 127 | Serial.println("Setting new characteristic value to \"" + newValue + "\""); 128 | 129 | // Set the characteristic's value to be the array of bytes that is actually a string. 130 | pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); 131 | } 132 | 133 | delay(1000); // Delay a second between loops. 134 | } // End of loop 135 | -------------------------------------------------------------------------------- /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 "BLEServer.h" 22 | #include "BLEUtils.h" 23 | #include "BLEBeacon.h" 24 | #include "esp_sleep.h" 25 | 26 | #define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up 27 | RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory 28 | RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | uint8_t temprature_sens_read(); 35 | //uint8_t g_phyFuns; 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | // See the following for generating UUIDs: 42 | // https://www.uuidgenerator.net/ 43 | BLEAdvertising *pAdvertising; 44 | struct timeval now; 45 | 46 | #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/) 47 | 48 | void setBeacon() { 49 | 50 | BLEBeacon oBeacon = BLEBeacon(); 51 | oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) 52 | oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); 53 | oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); 54 | oBeacon.setMinor(bootcount&0xFFFF); 55 | BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); 56 | BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); 57 | 58 | oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 59 | 60 | std::string strServiceData = ""; 61 | 62 | strServiceData += (char)26; // Len 63 | strServiceData += (char)0xFF; // Type 64 | strServiceData += oBeacon.getData(); 65 | oAdvertisementData.addData(strServiceData); 66 | 67 | pAdvertising->setAdvertisementData(oAdvertisementData); 68 | pAdvertising->setScanResponseData(oScanResponseData); 69 | 70 | } 71 | 72 | void setup() { 73 | 74 | 75 | Serial.begin(115200); 76 | gettimeofday(&now, NULL); 77 | 78 | Serial.printf("start ESP32 %d\n",bootcount++); 79 | 80 | Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); 81 | 82 | last = now.tv_sec; 83 | 84 | // Create the BLE Device 85 | BLEDevice::init(""); 86 | 87 | // Create the BLE Server 88 | BLEServer *pServer = BLEDevice::createServer(); 89 | 90 | pAdvertising = pServer->getAdvertising(); 91 | 92 | setBeacon(); 93 | // Start advertising 94 | pAdvertising->start(); 95 | Serial.println("Advertizing started..."); 96 | delay(100); 97 | pAdvertising->stop(); 98 | Serial.printf("enter deep sleep\n"); 99 | esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); 100 | Serial.printf("in deep sleep\n"); 101 | } 102 | 103 | void loop() { 104 | } 105 | -------------------------------------------------------------------------------- /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 | 6 | Create a BLE server that, once we receive a connection, will send periodic notifications. 7 | The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b 8 | And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 9 | 10 | The design of creating the BLE server is: 11 | 1. Create a BLE Server 12 | 2. Create a BLE Service 13 | 3. Create a BLE Characteristic on the Service 14 | 4. Create a BLE Descriptor on the characteristic 15 | 5. Start the service. 16 | 6. Start advertising. 17 | 18 | A connect hander associated with the server starts a background task that performs notification 19 | every couple of seconds. 20 | */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | BLEServer* pServer = NULL; 27 | BLECharacteristic* pCharacteristic = NULL; 28 | bool deviceConnected = false; 29 | bool oldDeviceConnected = false; 30 | uint8_t value = 0; 31 | 32 | // See the following for generating UUIDs: 33 | // https://www.uuidgenerator.net/ 34 | 35 | #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" 36 | #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" 37 | 38 | 39 | class MyServerCallbacks: public BLEServerCallbacks { 40 | void onConnect(BLEServer* pServer) { 41 | deviceConnected = true; 42 | }; 43 | 44 | void onDisconnect(BLEServer* pServer) { 45 | deviceConnected = false; 46 | } 47 | }; 48 | 49 | 50 | 51 | void setup() { 52 | Serial.begin(115200); 53 | 54 | // Create the BLE Device 55 | BLEDevice::init("MyESP32"); 56 | 57 | // Create the BLE Server 58 | pServer = BLEDevice::createServer(); 59 | pServer->setCallbacks(new MyServerCallbacks()); 60 | 61 | // Create the BLE Service 62 | BLEService *pService = pServer->createService(SERVICE_UUID); 63 | 64 | // Create a BLE Characteristic 65 | pCharacteristic = pService->createCharacteristic( 66 | CHARACTERISTIC_UUID, 67 | BLECharacteristic::PROPERTY_READ | 68 | BLECharacteristic::PROPERTY_WRITE | 69 | BLECharacteristic::PROPERTY_NOTIFY | 70 | BLECharacteristic::PROPERTY_INDICATE 71 | ); 72 | 73 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml 74 | // Create a BLE Descriptor 75 | pCharacteristic->addDescriptor(new BLE2902()); 76 | 77 | // Start the service 78 | pService->start(); 79 | 80 | // Start advertising 81 | pServer->getAdvertising()->start(); 82 | Serial.println("Waiting a client connection to notify..."); 83 | } 84 | 85 | void loop() { 86 | // notify changed value 87 | if (deviceConnected) { 88 | pCharacteristic->setValue(&value, 1); 89 | pCharacteristic->notify(); 90 | value++; 91 | delay(10); // bluetooth stack will go into congestion, if too many packets are sent 92 | } 93 | // disconnecting 94 | if (!deviceConnected && oldDeviceConnected) { 95 | delay(500); // give the bluetooth stack the chance to get things ready 96 | pServer->startAdvertising(); // restart advertising 97 | Serial.println("start advertising"); 98 | oldDeviceConnected = deviceConnected; 99 | } 100 | // connecting 101 | if (deviceConnected && !oldDeviceConnected) { 102 | // do stuff here on connecting 103 | oldDeviceConnected = deviceConnected; 104 | } 105 | } -------------------------------------------------------------------------------- /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 = 30; //In seconds 12 | 13 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 14 | void onResult(BLEAdvertisedDevice advertisedDevice) { 15 | Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); 16 | } 17 | }; 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | Serial.println("Scanning..."); 22 | 23 | BLEDevice::init(""); 24 | BLEScan* pBLEScan = BLEDevice::getScan(); //create new scan 25 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 26 | pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster 27 | BLEScanResults foundDevices = pBLEScan->start(scanTime); 28 | Serial.print("Devices found: "); 29 | Serial.println(foundDevices.getCount()); 30 | Serial.println("Scan done!"); 31 | } 32 | 33 | void loop() { 34 | // put your main code here, to run repeatedly: 35 | delay(2000); 36 | } -------------------------------------------------------------------------------- /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 | */ 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 | void setup() { 17 | Serial.begin(115200); 18 | Serial.println("Starting BLE work!"); 19 | 20 | BLEDevice::init("MyESP32"); 21 | BLEServer *pServer = BLEDevice::createServer(); 22 | BLEService *pService = pServer->createService(SERVICE_UUID); 23 | BLECharacteristic *pCharacteristic = pService->createCharacteristic( 24 | CHARACTERISTIC_UUID, 25 | BLECharacteristic::PROPERTY_READ | 26 | BLECharacteristic::PROPERTY_WRITE 27 | ); 28 | 29 | pCharacteristic->setValue("Hello World says Neil"); 30 | pService->start(); 31 | BLEAdvertising *pAdvertising = pServer->getAdvertising(); 32 | pAdvertising->start(); 33 | Serial.println("Characteristic defined! Now you can read it in your phone!"); 34 | } 35 | 36 | void loop() { 37 | // put your main code here, to run repeatedly: 38 | delay(2000); 39 | } -------------------------------------------------------------------------------- /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=0.4.12 3 | author=Neil Kolban 4 | maintainer=Neil Kolban 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=BLE.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h -------------------------------------------------------------------------------- /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 | #endif 25 | -------------------------------------------------------------------------------- /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 | 27 | }; // BLE2902 28 | 29 | #endif /* CONFIG_BT_ENABLED */ 30 | #endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ 31 | -------------------------------------------------------------------------------- /src/BLEAdvertising.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEAdvertising.cpp 3 | * 4 | * This class encapsulates advertising a BLE Server. 5 | * Created on: Jun 21, 2017 6 | * Author: kolban 7 | * 8 | * The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set 9 | * of properties that are advertised and has built a data structure that can be populated by the programmer. 10 | * This means that the programmer doesn't have to "mess with" the low level construction of a low level 11 | * BLE advertising frame. Many of the fields are determined for us while others we can set before starting 12 | * to advertise. 13 | * 14 | * Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters 15 | * upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer 16 | * set in the data will be advertised. 17 | * 18 | */ 19 | #include "sdkconfig.h" 20 | #if defined(CONFIG_BT_ENABLED) 21 | #include "BLEAdvertising.h" 22 | #include 23 | #include 24 | #include "GeneralUtils.h" 25 | 26 | #include 27 | 28 | #ifdef ARDUINO_ARCH_ESP32 29 | #include "esp32-hal-log.h" 30 | #endif 31 | 32 | static const char* LOG_TAG = "BLEAdvertising"; 33 | 34 | 35 | /** 36 | * @brief Construct a default advertising object. 37 | * 38 | */ 39 | BLEAdvertising::BLEAdvertising() { 40 | m_advData.set_scan_rsp = false; 41 | m_advData.include_name = true; 42 | m_advData.include_txpower = true; 43 | m_advData.min_interval = 0x20; 44 | m_advData.max_interval = 0x40; 45 | m_advData.appearance = 0x00; 46 | m_advData.manufacturer_len = 0; 47 | m_advData.p_manufacturer_data = nullptr; 48 | m_advData.service_data_len = 0; 49 | m_advData.p_service_data = nullptr; 50 | m_advData.service_uuid_len = 0; 51 | m_advData.p_service_uuid = nullptr; 52 | m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); 53 | 54 | m_advParams.adv_int_min = 0x20; 55 | m_advParams.adv_int_max = 0x40; 56 | m_advParams.adv_type = ADV_TYPE_IND; 57 | m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; 58 | m_advParams.channel_map = ADV_CHNL_ALL; 59 | m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; 60 | 61 | m_customAdvData = false; // No custom advertising data 62 | m_customScanResponseData = false; // No custom scan response data 63 | } // BLEAdvertising 64 | 65 | 66 | /** 67 | * @brief Add a service uuid to exposed list of services. 68 | * @param [in] serviceUUID The UUID of the service to expose. 69 | */ 70 | void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { 71 | m_serviceUUIDs.push_back(serviceUUID); 72 | } // addServiceUUID 73 | 74 | 75 | /** 76 | * @brief Add a service uuid to exposed list of services. 77 | * @param [in] serviceUUID The string representation of the service to expose. 78 | */ 79 | void BLEAdvertising::addServiceUUID(const char* serviceUUID) { 80 | addServiceUUID(BLEUUID(serviceUUID)); 81 | } // addServiceUUID 82 | 83 | 84 | /** 85 | * @brief Set the device appearance in the advertising data. 86 | * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: 87 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. 88 | * @param [in] appearance The appearance of the device in the advertising data. 89 | * @return N/A. 90 | */ 91 | void BLEAdvertising::setAppearance(uint16_t appearance) { 92 | m_advData.appearance = appearance; 93 | } // setAppearance 94 | 95 | void BLEAdvertising::setMinInterval(uint16_t mininterval) { 96 | m_advData.min_interval = mininterval; 97 | m_advParams.adv_int_min = mininterval; 98 | } // setMinInterval 99 | 100 | void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { 101 | m_advData.max_interval = maxinterval; 102 | m_advParams.adv_int_max = maxinterval; 103 | } // setMaxInterval 104 | 105 | 106 | /** 107 | * @brief Set the filtering for the scan filter. 108 | * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. 109 | * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. 110 | */ 111 | void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { 112 | ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); 113 | if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { 114 | m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; 115 | ESP_LOGD(LOG_TAG, "<< setScanFilter"); 116 | return; 117 | } 118 | if (scanRequestWhitelistOnly && !connectWhitelistOnly) { 119 | m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY; 120 | ESP_LOGD(LOG_TAG, "<< setScanFilter"); 121 | return; 122 | } 123 | if (!scanRequestWhitelistOnly && connectWhitelistOnly) { 124 | m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST; 125 | ESP_LOGD(LOG_TAG, "<< setScanFilter"); 126 | return; 127 | } 128 | if (scanRequestWhitelistOnly && connectWhitelistOnly) { 129 | m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST; 130 | ESP_LOGD(LOG_TAG, "<< setScanFilter"); 131 | return; 132 | } 133 | } // setScanFilter 134 | 135 | 136 | /** 137 | * @brief Set the advertisement data that is to be published in a regular advertisement. 138 | * @param [in] advertisementData The data to be advertised. 139 | */ 140 | void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) { 141 | ESP_LOGD(LOG_TAG, ">> setAdvertisementData"); 142 | esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw( 143 | (uint8_t*)advertisementData.getPayload().data(), 144 | advertisementData.getPayload().length()); 145 | if (errRc != ESP_OK) { 146 | ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); 147 | } 148 | m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. 149 | ESP_LOGD(LOG_TAG, "<< setAdvertisementData"); 150 | } // setAdvertisementData 151 | 152 | 153 | /** 154 | * @brief Set the advertisement data that is to be published in a scan response. 155 | * @param [in] advertisementData The data to be advertised. 156 | */ 157 | void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { 158 | ESP_LOGD(LOG_TAG, ">> setScanResponseData"); 159 | esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( 160 | (uint8_t*)advertisementData.getPayload().data(), 161 | advertisementData.getPayload().length()); 162 | if (errRc != ESP_OK) { 163 | ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); 164 | } 165 | m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. 166 | ESP_LOGD(LOG_TAG, "<< setScanResponseData"); 167 | } // setScanResponseData 168 | 169 | /** 170 | * @brief Start advertising. 171 | * Start advertising. 172 | * @return N/A. 173 | */ 174 | void BLEAdvertising::start() { 175 | ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); 176 | 177 | 178 | // We have a vector of service UUIDs that we wish to advertise. In order to use the 179 | // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) 180 | // representations. If we have 1 or more services to advertise then we allocate enough 181 | // storage to host them and then copy them in one at a time into the contiguous storage. 182 | int numServices = m_serviceUUIDs.size(); 183 | if (numServices > 0) { 184 | m_advData.service_uuid_len = 16*numServices; 185 | m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; 186 | uint8_t* p = m_advData.p_service_uuid; 187 | for (int i=0; iuuid.uuid128, 16); 191 | p+=16; 192 | } 193 | } else { 194 | m_advData.service_uuid_len = 0; 195 | ESP_LOGD(LOG_TAG, "- no services advertised"); 196 | } 197 | 198 | esp_err_t errRc; 199 | 200 | if (m_customAdvData == false) { 201 | // Set the configuration for advertising. 202 | m_advData.set_scan_rsp = false; 203 | errRc = ::esp_ble_gap_config_adv_data(&m_advData); 204 | if (errRc != ESP_OK) { 205 | ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 206 | return; 207 | } 208 | } 209 | 210 | if (m_customScanResponseData == false) { 211 | m_advData.set_scan_rsp = true; 212 | errRc = ::esp_ble_gap_config_adv_data(&m_advData); 213 | if (errRc != ESP_OK) { 214 | ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 215 | return; 216 | } 217 | } 218 | 219 | // If we had services to advertise then we previously allocated some storage for them. 220 | // Here we release that storage. 221 | if (m_advData.service_uuid_len > 0) { 222 | delete[] m_advData.p_service_uuid; 223 | m_advData.p_service_uuid = nullptr; 224 | } 225 | 226 | // Start advertising. 227 | errRc = ::esp_ble_gap_start_advertising(&m_advParams); 228 | if (errRc != ESP_OK) { 229 | ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 230 | return; 231 | } 232 | ESP_LOGD(LOG_TAG, "<< start"); 233 | } // start 234 | 235 | 236 | /** 237 | * @brief Stop advertising. 238 | * Stop advertising. 239 | * @return N/A. 240 | */ 241 | void BLEAdvertising::stop() { 242 | ESP_LOGD(LOG_TAG, ">> stop"); 243 | esp_err_t errRc = ::esp_ble_gap_stop_advertising(); 244 | if (errRc != ESP_OK) { 245 | ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 246 | return; 247 | } 248 | ESP_LOGD(LOG_TAG, "<< stop"); 249 | } // stop 250 | 251 | /** 252 | * @brief Add data to the payload to be advertised. 253 | * @param [in] data The data to be added to the payload. 254 | */ 255 | void BLEAdvertisementData::addData(std::string data) { 256 | if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { 257 | return; 258 | } 259 | m_payload.append(data); 260 | } // addData 261 | 262 | 263 | /** 264 | * @brief Set the appearance. 265 | * @param [in] appearance The appearance code value. 266 | * 267 | * See also: 268 | * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml 269 | */ 270 | void BLEAdvertisementData::setAppearance(uint16_t appearance) { 271 | char cdata[2]; 272 | cdata[0] = 3; 273 | cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 274 | addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); 275 | } // setAppearance 276 | 277 | 278 | /** 279 | * @brief Set the complete services. 280 | * @param [in] uuid The single service to advertise. 281 | */ 282 | void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { 283 | char cdata[2]; 284 | switch(uuid.bitSize()) { 285 | case 16: { 286 | // [Len] [0x02] [LL] [HH] 287 | cdata[0] = 3; 288 | cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 289 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); 290 | break; 291 | } 292 | 293 | case 32: { 294 | // [Len] [0x04] [LL] [LL] [HH] [HH] 295 | cdata[0] = 5; 296 | cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 297 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); 298 | break; 299 | } 300 | 301 | case 128: { 302 | // [Len] [0x04] [0] [1] ... [15] 303 | cdata[0] = 17; 304 | cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 305 | addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); 306 | break; 307 | } 308 | 309 | default: 310 | return; 311 | } 312 | } // setCompleteServices 313 | 314 | 315 | /** 316 | * @brief Set the advertisement flags. 317 | * @param [in] The flags to be set in the advertisement. 318 | * 319 | * * ESP_BLE_ADV_FLAG_LIMIT_DISC 320 | * * ESP_BLE_ADV_FLAG_GEN_DISC 321 | * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT 322 | * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT 323 | * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT 324 | * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC 325 | */ 326 | void BLEAdvertisementData::setFlags(uint8_t flag) { 327 | char cdata[3]; 328 | cdata[0] = 2; 329 | cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 330 | cdata[2] = flag; 331 | addData(std::string(cdata, 3)); 332 | } // setFlag 333 | 334 | 335 | 336 | /** 337 | * @brief Set manufacturer specific data. 338 | * @param [in] data Manufacturer data. 339 | */ 340 | void BLEAdvertisementData::setManufacturerData(std::string data) { 341 | ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); 342 | char cdata[2]; 343 | cdata[0] = data.length() + 1; 344 | cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff 345 | addData(std::string(cdata, 2) + data); 346 | ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); 347 | } // setManufacturerData 348 | 349 | 350 | /** 351 | * @brief Set the name. 352 | * @param [in] The complete name of the device. 353 | */ 354 | void BLEAdvertisementData::setName(std::string name) { 355 | ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); 356 | char cdata[2]; 357 | cdata[0] = name.length() + 1; 358 | cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 359 | addData(std::string(cdata, 2) + name); 360 | ESP_LOGD("BLEAdvertisementData", "<< setName"); 361 | } // setName 362 | 363 | 364 | /** 365 | * @brief Set the partial services. 366 | * @param [in] uuid The single service to advertise. 367 | */ 368 | void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { 369 | char cdata[2]; 370 | switch(uuid.bitSize()) { 371 | case 16: { 372 | // [Len] [0x02] [LL] [HH] 373 | cdata[0] = 3; 374 | cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 375 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); 376 | break; 377 | } 378 | 379 | case 32: { 380 | // [Len] [0x04] [LL] [LL] [HH] [HH] 381 | cdata[0] = 5; 382 | cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 383 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); 384 | break; 385 | } 386 | 387 | case 128: { 388 | // [Len] [0x04] [0] [1] ... [15] 389 | cdata[0] = 17; 390 | cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 391 | addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); 392 | break; 393 | } 394 | 395 | default: 396 | return; 397 | } 398 | } // setPartialServices 399 | 400 | 401 | /** 402 | * @brief Set the service data (UUID + data) 403 | * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. 404 | * @param [in] data The data to be associated with the service data advert. 405 | */ 406 | void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { 407 | char cdata[2]; 408 | switch(uuid.bitSize()) { 409 | case 16: { 410 | // [Len] [0x16] [UUID16] data 411 | cdata[0] = data.length() + 3; 412 | cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 413 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data); 414 | break; 415 | } 416 | 417 | case 32: { 418 | // [Len] [0x20] [UUID32] data 419 | cdata[0] = data.length() + 5; 420 | cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 421 | addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data); 422 | break; 423 | } 424 | 425 | case 128: { 426 | // [Len] [0x21] [UUID128] data 427 | cdata[0] = data.length() + 17; 428 | cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 429 | addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data); 430 | break; 431 | } 432 | 433 | default: 434 | return; 435 | } 436 | } // setServiceData 437 | 438 | 439 | /** 440 | * @brief Set the short name. 441 | * @param [in] The short name of the device. 442 | */ 443 | void BLEAdvertisementData::setShortName(std::string name) { 444 | ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); 445 | char cdata[2]; 446 | cdata[0] = name.length() + 1; 447 | cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 448 | addData(std::string(cdata, 2) + name); 449 | ESP_LOGD("BLEAdvertisementData", "<< setShortName"); 450 | } // setShortName 451 | 452 | 453 | 454 | /** 455 | * @brief Retrieve the payload that is to be advertised. 456 | * @return The payload that is to be advertised. 457 | */ 458 | std::string BLEAdvertisementData::getPayload() { 459 | return m_payload; 460 | } // getPayload 461 | 462 | 463 | #endif /* CONFIG_BT_ENABLED */ 464 | -------------------------------------------------------------------------------- /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 | 16 | /** 17 | * @brief Advertisement data set by the programmer to be published by the %BLE server. 18 | */ 19 | class BLEAdvertisementData { 20 | // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will 21 | // be exposed on demand/request or as time permits. 22 | // 23 | public: 24 | void setAppearance(uint16_t appearance); 25 | void setCompleteServices(BLEUUID uuid); 26 | void setFlags(uint8_t); 27 | void setManufacturerData(std::string data); 28 | void setName(std::string name); 29 | void setPartialServices(BLEUUID uuid); 30 | void setServiceData(BLEUUID uuid, std::string data); 31 | void setShortName(std::string name); 32 | void addData(std::string data); // Add data to the payload. 33 | std::string getPayload(); // Retrieve the current advert payload. 34 | 35 | private: 36 | friend class BLEAdvertising; 37 | std::string m_payload; // The payload of the advertisement. 38 | }; // BLEAdvertisementData 39 | 40 | 41 | /** 42 | * @brief Perform and manage %BLE advertising. 43 | * 44 | * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. 45 | */ 46 | class BLEAdvertising { 47 | public: 48 | BLEAdvertising(); 49 | void addServiceUUID(BLEUUID serviceUUID); 50 | void addServiceUUID(const char* serviceUUID); 51 | void start(); 52 | void stop(); 53 | void setAppearance(uint16_t appearance); 54 | void setMaxInterval(uint16_t maxinterval); 55 | void setMinInterval(uint16_t mininterval); 56 | void setAdvertisementData(BLEAdvertisementData& advertisementData); 57 | void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); 58 | void setScanResponseData(BLEAdvertisementData& advertisementData); 59 | 60 | private: 61 | esp_ble_adv_data_t m_advData; 62 | esp_ble_adv_params_t m_advParams; 63 | std::vector m_serviceUUIDs; 64 | bool m_customAdvData; // Are we using custom advertising data? 65 | bool m_customScanResponseData; // Are we using custom scan response data? 66 | }; 67 | #endif /* CONFIG_BT_ENABLED */ 68 | #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ 69 | -------------------------------------------------------------------------------- /src/BLECharacteristic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLECharacteristic.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 16 | #include "BLECharacteristic.h" 17 | #include "BLEService.h" 18 | #include "BLEDevice.h" 19 | #include "BLE2902.h" 20 | #include "GeneralUtils.h" 21 | #ifdef ARDUINO_ARCH_ESP32 22 | #include "esp32-hal-log.h" 23 | #endif 24 | 25 | static const char* LOG_TAG = "BLECharacteristic"; 26 | 27 | #define NULL_HANDLE (0xffff) 28 | 29 | 30 | /** 31 | * @brief Construct a characteristic 32 | * @param [in] uuid - UUID (const char*) for the characteristic. 33 | * @param [in] properties - Properties for the characteristic. 34 | */ 35 | BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) { 36 | } 37 | 38 | /** 39 | * @brief Construct a characteristic 40 | * @param [in] uuid - UUID for the characteristic. 41 | * @param [in] properties - Properties for the characteristic. 42 | */ 43 | BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { 44 | m_bleUUID = uuid; 45 | m_handle = NULL_HANDLE; 46 | m_properties = (esp_gatt_char_prop_t)0; 47 | m_pCallbacks = nullptr; 48 | 49 | setBroadcastProperty((properties & PROPERTY_BROADCAST) !=0); 50 | setReadProperty((properties & PROPERTY_READ) !=0); 51 | setWriteProperty((properties & PROPERTY_WRITE) !=0); 52 | setNotifyProperty((properties & PROPERTY_NOTIFY) !=0); 53 | setIndicateProperty((properties & PROPERTY_INDICATE) !=0); 54 | setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) !=0); 55 | } // BLECharacteristic 56 | 57 | /** 58 | * @brief Destructor. 59 | */ 60 | BLECharacteristic::~BLECharacteristic() { 61 | //free(m_value.attr_value); // Release the storage for the value. 62 | } // ~BLECharacteristic 63 | 64 | 65 | /** 66 | * @brief Associate a descriptor with this characteristic. 67 | * @param [in] pDescriptor 68 | * @return N/A. 69 | */ 70 | void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) { 71 | m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); 72 | ESP_LOGD(LOG_TAG, "<< addDescriptor()"); 73 | } // addDescriptor 74 | 75 | 76 | /** 77 | * @brief Register a new characteristic with the ESP runtime. 78 | * @param [in] pService The service with which to associate this characteristic. 79 | */ 80 | void BLECharacteristic::executeCreate(BLEService* pService) { 81 | ESP_LOGD(LOG_TAG, ">> executeCreate()"); 82 | 83 | if (m_handle != NULL_HANDLE) { 84 | ESP_LOGE(LOG_TAG, "Characteristic already has a handle."); 85 | return; 86 | } 87 | 88 | m_pService = pService; // Save the service for to which this characteristic belongs. 89 | 90 | esp_attr_control_t control; 91 | control.auto_rsp = ESP_GATT_RSP_BY_APP; 92 | 93 | m_semaphoreCreateEvt.take("executeCreate"); 94 | 95 | /* 96 | esp_attr_value_t value; 97 | value.attr_len = m_value.getLength(); 98 | value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; 99 | value.attr_value = m_value.getData(); 100 | */ 101 | 102 | esp_err_t errRc = ::esp_ble_gatts_add_char( 103 | m_pService->getHandle(), 104 | getUUID().getNative(), 105 | static_cast(m_permissions), 106 | getProperties(), 107 | //&value, 108 | nullptr, 109 | &control); // Whether to auto respond or not. 110 | 111 | if (errRc != ESP_OK) { 112 | ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 113 | return; 114 | } 115 | 116 | m_semaphoreCreateEvt.wait("executeCreate"); 117 | 118 | // Now that we have registered the characteristic, we must also register all the descriptors associated with this 119 | // characteristic. We iterate through each of those and invoke the registration call to register them with the 120 | // ESP environment. 121 | 122 | BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); 123 | 124 | while (pDescriptor != nullptr) { 125 | pDescriptor->executeCreate(this); 126 | pDescriptor = m_descriptorMap.getNext(); 127 | } // End while 128 | 129 | ESP_LOGD(LOG_TAG, "<< executeCreate"); 130 | } // executeCreate 131 | 132 | 133 | 134 | /** 135 | * @brief Get the handle of the characteristic. 136 | * @return The handle of the characteristic. 137 | */ 138 | uint16_t BLECharacteristic::getHandle() { 139 | return m_handle; 140 | } // getHandle 141 | esp_gatt_char_prop_t BLECharacteristic::getProperties() { 142 | return m_properties; 143 | } // getProperties 144 | 145 | 146 | /** 147 | * @brief Get the service associated with this characteristic. 148 | */ 149 | BLEService* BLECharacteristic::getService() { 150 | return m_pService; 151 | } // getService 152 | 153 | 154 | /** 155 | * @brief Get the UUID of the characteristic. 156 | * @return The UUID of the characteristic. 157 | */ 158 | BLEUUID BLECharacteristic::getUUID() { 159 | return m_bleUUID; 160 | } // getUUID 161 | 162 | 163 | /** 164 | * @brief Retrieve the current value of the characteristic. 165 | * @return A pointer to storage containing the current characteristic value. 166 | */ 167 | std::string BLECharacteristic::getValue() { 168 | return m_value.getValue(); 169 | } // getValue 170 | 171 | 172 | /** 173 | * Handle a GATT server event. 174 | */ 175 | void BLECharacteristic::handleGATTServerEvent( 176 | esp_gatts_cb_event_t event, 177 | esp_gatt_if_t gatts_if, 178 | esp_ble_gatts_cb_param_t* param) { 179 | 180 | switch(event) { 181 | // Events handled: 182 | // 183 | // ESP_GATTS_ADD_CHAR_EVT 184 | // ESP_GATTS_CONF_EVT 185 | // ESP_GATTS_CONNECT_EVT 186 | // ESP_GATTS_DISCONNECT_EVT 187 | // ESP_GATTS_EXEC_WRITE_EVT 188 | // ESP_GATTS_READ_EVT 189 | // ESP_GATTS_WRITE_EVT 190 | 191 | // 192 | // ESP_GATTS_EXEC_WRITE_EVT 193 | // When we receive this event it is an indication that a previous write long needs to be committed. 194 | // 195 | // exec_write: 196 | // - uint16_t conn_id 197 | // - uint32_t trans_id 198 | // - esp_bd_addr_t bda 199 | // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL 200 | // 201 | case ESP_GATTS_EXEC_WRITE_EVT: { 202 | if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { 203 | m_value.commit(); 204 | if (m_pCallbacks != nullptr) { 205 | m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. 206 | } 207 | } else { 208 | m_value.cancel(); 209 | } 210 | 211 | esp_err_t errRc = ::esp_ble_gatts_send_response( 212 | gatts_if, 213 | param->write.conn_id, 214 | param->write.trans_id, ESP_GATT_OK, nullptr); 215 | if (errRc != ESP_OK) { 216 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 217 | } 218 | break; 219 | } // ESP_GATTS_EXEC_WRITE_EVT 220 | 221 | 222 | // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. 223 | // add_char: 224 | // - esp_gatt_status_t status 225 | // - uint16_t attr_handle 226 | // - uint16_t service_handle 227 | // - esp_bt_uuid_t char_uuid 228 | case ESP_GATTS_ADD_CHAR_EVT: { 229 | if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && 230 | getHandle() == param->add_char.attr_handle && 231 | getService()->getHandle()==param->add_char.service_handle) { 232 | m_semaphoreCreateEvt.give(); 233 | } 234 | break; 235 | } // ESP_GATTS_ADD_CHAR_EVT 236 | 237 | 238 | // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. 239 | // 240 | // write: 241 | // - uint16_t conn_id 242 | // - uint16_t trans_id 243 | // - esp_bd_addr_t bda 244 | // - uint16_t handle 245 | // - uint16_t offset 246 | // - bool need_rsp 247 | // - bool is_prep 248 | // - uint16_t len 249 | // - uint8_t *value 250 | // 251 | case ESP_GATTS_WRITE_EVT: { 252 | // We check if this write request is for us by comparing the handles in the event. If it is for us 253 | // we save the new value. Next we look at the need_rsp flag which indicates whether or not we need 254 | // to send a response. If we do, then we formulate a response and send it. 255 | if (param->write.handle == m_handle) { 256 | if (param->write.is_prep) { 257 | m_value.addPart(param->write.value, param->write.len); 258 | } else { 259 | setValue(param->write.value, param->write.len); 260 | } 261 | 262 | ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", 263 | getHandle(), getUUID().toString().c_str()); 264 | 265 | if (param->write.need_rsp) { 266 | esp_gatt_rsp_t rsp; 267 | 268 | rsp.attr_value.len = param->write.len; 269 | rsp.attr_value.handle = m_handle; 270 | rsp.attr_value.offset = param->write.offset; 271 | rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 272 | memcpy(rsp.attr_value.value, param->write.value, param->write.len); 273 | 274 | esp_err_t errRc = ::esp_ble_gatts_send_response( 275 | gatts_if, 276 | param->write.conn_id, 277 | param->write.trans_id, ESP_GATT_OK, &rsp); 278 | if (errRc != ESP_OK) { 279 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 280 | } 281 | } // Response needed 282 | 283 | if (m_pCallbacks != nullptr && param->write.is_prep != true) { 284 | m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. 285 | } 286 | } // Match on handles. 287 | break; 288 | } // ESP_GATTS_WRITE_EVT 289 | 290 | 291 | // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. 292 | // 293 | // read: 294 | // - uint16_t conn_id 295 | // - uint32_t trans_id 296 | // - esp_bd_addr_t bda 297 | // - uint16_t handle 298 | // - uint16_t offset 299 | // - bool is_long 300 | // - bool need_rsp 301 | // 302 | case ESP_GATTS_READ_EVT: { 303 | if (param->read.handle == m_handle) { 304 | 305 | 306 | 307 | // Here's an interesting thing. The read request has the option of saying whether we need a response 308 | // or not. What would it "mean" to receive a read request and NOT send a response back? That feels like 309 | // a very strange read. 310 | // 311 | // We have to handle the case where the data we wish to send back to the client is greater than the maximum 312 | // packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes. 313 | // The apparent algorithm is as follows: 314 | // 315 | // If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. 316 | // If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than 317 | // 22 bytes, then we "just" send it and thats the end of the story. 318 | // If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. 319 | // If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. 320 | // Because of follow on request processing, we need to maintain an offset of how much data we have already sent 321 | // so that when a follow on request arrives, we know where to start in the data to send the next sequence. 322 | // Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response. 323 | // If our payload is divisible by 22 then the last response will be a response of 0 bytes in length. 324 | // 325 | // The following code has deliberately not been factored to make it fewer statements because this would cloud the 326 | // the logic flow comprehension. 327 | // 328 | // TODO requires some more research to confirm that 512 is max PDU like in bluetooth specs 329 | uint16_t maxOffset = BLEDevice::getMTU() - 1; 330 | if (BLEDevice::getMTU() > 512) { 331 | maxOffset = 512; 332 | } 333 | if (param->read.need_rsp) { 334 | ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); 335 | esp_gatt_rsp_t rsp; 336 | 337 | if (param->read.is_long) { 338 | std::string value = m_value.getValue(); 339 | 340 | if (value.length() - m_value.getReadOffset() < maxOffset) { 341 | // This is the last in the chain 342 | rsp.attr_value.len = value.length() - m_value.getReadOffset(); 343 | rsp.attr_value.offset = m_value.getReadOffset(); 344 | memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); 345 | m_value.setReadOffset(0); 346 | } else { 347 | // There will be more to come. 348 | rsp.attr_value.len = maxOffset; 349 | rsp.attr_value.offset = m_value.getReadOffset(); 350 | memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); 351 | m_value.setReadOffset(rsp.attr_value.offset + maxOffset); 352 | } 353 | } else { // read.is_long == false 354 | 355 | if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback 356 | m_pCallbacks->onRead(this); // Invoke the read callback. 357 | } 358 | 359 | std::string value = m_value.getValue(); 360 | 361 | if (value.length()+1 > maxOffset) { 362 | // Too big for a single shot entry. 363 | m_value.setReadOffset(maxOffset); 364 | rsp.attr_value.len = maxOffset; 365 | rsp.attr_value.offset = 0; 366 | memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); 367 | } else { 368 | // Will fit in a single packet with no callbacks required. 369 | rsp.attr_value.len = value.length(); 370 | rsp.attr_value.offset = 0; 371 | memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); 372 | } 373 | } 374 | rsp.attr_value.handle = param->read.handle; 375 | rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 376 | 377 | esp_err_t errRc = ::esp_ble_gatts_send_response( 378 | gatts_if, param->read.conn_id, 379 | param->read.trans_id, 380 | ESP_GATT_OK, 381 | &rsp); 382 | if (errRc != ESP_OK) { 383 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 384 | } 385 | } // Response needed 386 | } // Handle matches this characteristic. 387 | break; 388 | } // ESP_GATTS_READ_EVT 389 | 390 | 391 | // ESP_GATTS_CONF_EVT 392 | // 393 | // conf: 394 | // - esp_gatt_status_t status – The status code. 395 | // - uint16_t conn_id – The connection used. 396 | // 397 | case ESP_GATTS_CONF_EVT: { 398 | m_semaphoreConfEvt.give(); 399 | break; 400 | } 401 | 402 | case ESP_GATTS_CONNECT_EVT: { 403 | m_semaphoreConfEvt.give(); 404 | break; 405 | } 406 | 407 | case ESP_GATTS_DISCONNECT_EVT: { 408 | m_semaphoreConfEvt.give(); 409 | break; 410 | } 411 | 412 | default: { 413 | break; 414 | } // default 415 | 416 | } // switch event 417 | 418 | // Give each of the descriptors associated with this characteristic the opportunity to handle the 419 | // event. 420 | 421 | m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); 422 | ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); 423 | } // handleGATTServerEvent 424 | 425 | 426 | /** 427 | * @brief Set the permission to broadcast. 428 | * A characteristics has properties associated with it which define what it is capable of doing. 429 | * One of these is the broadcast flag. 430 | * @param [in] value The flag value of the property. 431 | * @return N/A 432 | */ 433 | void BLECharacteristic::setBroadcastProperty(bool value) { 434 | //ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value); 435 | if (value) { 436 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); 437 | } else { 438 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); 439 | } 440 | } // setBroadcastProperty 441 | 442 | 443 | /** 444 | * @brief Set the callback handlers for this characteristic. 445 | * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. 446 | */ 447 | void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { 448 | ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks); 449 | m_pCallbacks = pCallbacks; 450 | ESP_LOGD(LOG_TAG, "<< setCallbacks"); 451 | } // setCallbacks 452 | 453 | 454 | /** 455 | * @brief Set the BLE handle associated with this characteristic. 456 | * A user program will request that a characteristic be created against a service. When the characteristic has been 457 | * registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the 458 | * server/service but it is told to the service, not the characteristic associated with the service. This internally 459 | * exposed function can be invoked by the service against this model of the characteristic to allow the characteristic 460 | * to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events 461 | * that will be propagated down to it which contain a handle value and now know that the event is destined for it. 462 | * @param [in] handle The handle associated with this characteristic. 463 | */ 464 | void BLECharacteristic::setHandle(uint16_t handle) { 465 | ESP_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str()); 466 | m_handle = handle; 467 | ESP_LOGD(LOG_TAG, "<< setHandle"); 468 | } // setHandle 469 | 470 | 471 | /** 472 | * @brief Set the Indicate property value. 473 | * @param [in] value Set to true if we are to allow indicate messages. 474 | */ 475 | void BLECharacteristic::setIndicateProperty(bool value) { 476 | //ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value); 477 | if (value) { 478 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); 479 | } else { 480 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); 481 | } 482 | } // setIndicateProperty 483 | 484 | 485 | /** 486 | * @brief Set the Notify property value. 487 | * @param [in] value Set to true if we are to allow notification messages. 488 | */ 489 | void BLECharacteristic::setNotifyProperty(bool value) { 490 | //ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value); 491 | if (value) { 492 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); 493 | } else { 494 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); 495 | } 496 | } // setNotifyProperty 497 | 498 | 499 | /** 500 | * @brief Set the Read property value. 501 | * @param [in] value Set to true if we are to allow reads. 502 | */ 503 | void BLECharacteristic::setReadProperty(bool value) { 504 | //ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value); 505 | if (value) { 506 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ); 507 | } else { 508 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); 509 | } 510 | } // setReadProperty 511 | 512 | 513 | /** 514 | * @brief Set the value of the characteristic. 515 | * @param [in] data The data to set for the characteristic. 516 | * @param [in] length The length of the data in bytes. 517 | */ 518 | void BLECharacteristic::setValue(uint8_t* data, size_t length) { 519 | if (length > ESP_GATT_MAX_ATTR_LEN) { 520 | ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); 521 | return; 522 | } 523 | m_value.setValue(data, length); 524 | ESP_LOGD(LOG_TAG, "<< setValue"); 525 | } // setValue 526 | 527 | 528 | /** 529 | * @brief Set the value of the characteristic from string data. 530 | * We set the value of the characteristic from the bytes contained in the 531 | * string. 532 | * @param [in] Set the value of the characteristic. 533 | * @return N/A. 534 | */ 535 | void BLECharacteristic::setValue(std::string value) { 536 | setValue((uint8_t*)(value.data()), value.length()); 537 | } // setValue 538 | 539 | 540 | /** 541 | * @brief Set the Write No Response property value. 542 | * @param [in] value Set to true if we are to allow writes with no response. 543 | */ 544 | void BLECharacteristic::setWriteNoResponseProperty(bool value) { 545 | //ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value); 546 | if (value) { 547 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); 548 | } else { 549 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); 550 | } 551 | } // setWriteNoResponseProperty 552 | 553 | 554 | /** 555 | * @brief Set the Write property value. 556 | * @param [in] value Set to true if we are to allow writes. 557 | */ 558 | void BLECharacteristic::setWriteProperty(bool value) { 559 | //ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value); 560 | if (value) { 561 | m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); 562 | } else { 563 | m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); 564 | } 565 | } // setWriteProperty 566 | 567 | 568 | 569 | BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} 570 | 571 | 572 | /** 573 | * @brief Callback function to support a read request. 574 | * @param [in] pCharacteristic The characteristic that is the source of the event. 575 | */ 576 | void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { 577 | ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); 578 | ESP_LOGD("BLECharacteristicCallbacks", "<< onRead"); 579 | } // onRead 580 | 581 | 582 | /** 583 | * @brief Callback function to support a write request. 584 | * @param [in] pCharacteristic The characteristic that is the source of the event. 585 | */ 586 | void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) { 587 | ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); 588 | ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite"); 589 | } // onWrite 590 | 591 | #endif /* CONFIG_BT_ENABLED */ 592 | -------------------------------------------------------------------------------- /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( 38 | esp_gatts_cb_event_t event, 39 | esp_gatt_if_t gatts_if, 40 | esp_ble_gatts_cb_param_t* param); 41 | BLEDescriptor* getFirst(); 42 | BLEDescriptor* getNext(); 43 | private: 44 | std::map m_uuidMap; 45 | std::map m_handleMap; 46 | std::map::iterator m_iterator; 47 | }; 48 | 49 | 50 | /** 51 | * @brief The model of a %BLE Characteristic. 52 | * 53 | * A %BLE Characteristic is an identified value container that manages a value. It is exposed by a %BLE server and 54 | * can be read and written to by a %BLE client. 55 | */ 56 | class BLECharacteristic { 57 | public: 58 | BLECharacteristic(const char* uuid, uint32_t properties = 0); 59 | BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); 60 | virtual ~BLECharacteristic(); 61 | 62 | void addDescriptor(BLEDescriptor* pDescriptor); 63 | //size_t getLength(); 64 | BLEUUID getUUID(); 65 | std::string getValue(); 66 | 67 | void setBroadcastProperty(bool value); 68 | void setCallbacks(BLECharacteristicCallbacks* pCallbacks); 69 | void setIndicateProperty(bool value); 70 | void setNotifyProperty(bool value); 71 | void setReadProperty(bool value); 72 | void setValue(uint8_t* data, size_t size); 73 | void setValue(std::string value); 74 | void setWriteProperty(bool value); 75 | void setWriteNoResponseProperty(bool value); 76 | uint16_t getHandle(); 77 | 78 | static const uint32_t PROPERTY_READ = 1<<0; 79 | static const uint32_t PROPERTY_WRITE = 1<<1; 80 | static const uint32_t PROPERTY_NOTIFY = 1<<2; 81 | static const uint32_t PROPERTY_BROADCAST = 1<<3; 82 | static const uint32_t PROPERTY_INDICATE = 1<<4; 83 | static const uint32_t PROPERTY_WRITE_NR = 1<<5; 84 | 85 | private: 86 | 87 | friend class BLEServer; 88 | friend class BLEService; 89 | friend class BLEDescriptor; 90 | friend class BLECharacteristicMap; 91 | 92 | BLEUUID m_bleUUID; 93 | BLEDescriptorMap m_descriptorMap; 94 | uint16_t m_handle; 95 | esp_gatt_char_prop_t m_properties; 96 | BLECharacteristicCallbacks* m_pCallbacks; 97 | BLEService* m_pService; 98 | BLEValue m_value; 99 | esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; 100 | 101 | void handleGATTServerEvent( 102 | esp_gatts_cb_event_t event, 103 | esp_gatt_if_t gatts_if, 104 | esp_ble_gatts_cb_param_t* param); 105 | 106 | void executeCreate(BLEService* pService); 107 | esp_gatt_char_prop_t getProperties(); 108 | BLEService* getService(); 109 | void setHandle(uint16_t handle); 110 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 111 | FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); 112 | }; // BLECharacteristic 113 | 114 | 115 | /** 116 | * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. 117 | * 118 | * When a server application creates a %BLE characteristic, we may wish to be informed when there is either 119 | * a read or write request to the characteristic's value. An application can register a 120 | * sub-classed instance of this class and will be notified when such an event happens. 121 | */ 122 | class BLECharacteristicCallbacks { 123 | public: 124 | virtual ~BLECharacteristicCallbacks(); 125 | virtual void onRead(BLECharacteristic* pCharacteristic); 126 | virtual void onWrite(BLECharacteristic* pCharacteristic); 127 | }; 128 | #endif /* CONFIG_BT_ENABLED */ 129 | #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ 130 | -------------------------------------------------------------------------------- /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()) { 60 | return nullptr; 61 | } 62 | BLECharacteristic* pRet = m_iterator->first; 63 | m_iterator++; 64 | return pRet; 65 | } // getFirst 66 | 67 | 68 | /** 69 | * @brief Get the next characteristic in the map. 70 | * @return The next characteristic in the map. 71 | */ 72 | BLECharacteristic* BLECharacteristicMap::getNext() { 73 | if (m_iterator == m_uuidMap.end()) { 74 | return nullptr; 75 | } 76 | BLECharacteristic* pRet = m_iterator->first; 77 | m_iterator++; 78 | return pRet; 79 | } // getNext 80 | 81 | 82 | /** 83 | * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping 84 | * @param [in] event 85 | * @param [in] gatts_if 86 | * @param [in] param 87 | */ 88 | void BLECharacteristicMap::handleGATTServerEvent( 89 | esp_gatts_cb_event_t event, 90 | esp_gatt_if_t gatts_if, 91 | esp_ble_gatts_cb_param_t* param) { 92 | // Invoke the handler for every Service we have. 93 | for (auto &myPair : m_uuidMap) { 94 | myPair.first->handleGATTServerEvent(event, gatts_if, param); 95 | } 96 | } // handleGATTServerEvent 97 | 98 | 99 | /** 100 | * @brief Set the characteristic by handle. 101 | * @param [in] handle The handle of the characteristic. 102 | * @param [in] characteristic The characteristic to cache. 103 | * @return N/A. 104 | */ 105 | void BLECharacteristicMap::setByHandle(uint16_t handle, 106 | BLECharacteristic *characteristic) { 107 | m_handleMap.insert(std::pair(handle, characteristic)); 108 | } // setByHandle 109 | 110 | 111 | /** 112 | * @brief Set the characteristic by UUID. 113 | * @param [in] uuid The uuid of the characteristic. 114 | * @param [in] characteristic The characteristic to cache. 115 | * @return N/A. 116 | */ 117 | void BLECharacteristicMap::setByUUID( 118 | BLECharacteristic *pCharacteristic, 119 | BLEUUID uuid) { 120 | m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); 121 | } // setByUUID 122 | 123 | 124 | /** 125 | * @brief Return a string representation of the characteristic map. 126 | * @return A string representation of the characteristic map. 127 | */ 128 | std::string BLECharacteristicMap::toString() { 129 | std::stringstream stringStream; 130 | stringStream << std::hex << std::setfill('0'); 131 | int count=0; 132 | for (auto &myPair: m_uuidMap) { 133 | if (count > 0) { 134 | stringStream << "\n"; 135 | } 136 | count++; 137 | stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); 138 | } 139 | return stringStream.str(); 140 | } // toString 141 | 142 | 143 | #endif /* CONFIG_BT_ENABLED */ 144 | -------------------------------------------------------------------------------- /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 16 | #include "BLEService.h" 17 | #include "BLEDescriptor.h" 18 | #include "GeneralUtils.h" 19 | #ifdef ARDUINO_ARCH_ESP32 20 | #include "esp32-hal-log.h" 21 | #endif 22 | 23 | static const char* LOG_TAG = "BLEDescriptor"; 24 | 25 | 26 | #define NULL_HANDLE (0xffff) 27 | 28 | 29 | /** 30 | * @brief BLEDescriptor constructor. 31 | */ 32 | BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) { 33 | } 34 | 35 | /** 36 | * @brief BLEDescriptor constructor. 37 | */ 38 | BLEDescriptor::BLEDescriptor(BLEUUID uuid) { 39 | m_bleUUID = uuid; 40 | m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. 41 | m_value.attr_len = 0; // Initial length is 0. 42 | m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; // Maximum length of the data. 43 | m_handle = NULL_HANDLE; // Handle is initially unknown. 44 | m_pCharacteristic = nullptr; // No initial characteristic. 45 | m_pCallback = nullptr; // No initial callback. 46 | 47 | } // BLEDescriptor 48 | 49 | 50 | /** 51 | * @brief BLEDescriptor destructor. 52 | */ 53 | BLEDescriptor::~BLEDescriptor() { 54 | free(m_value.attr_value); // Release the storage we created in the constructor. 55 | } // ~BLEDescriptor 56 | 57 | 58 | /** 59 | * @brief Execute the creation of the descriptor with the BLE runtime in ESP. 60 | * @param [in] pCharacteristic The characteristic to which to register this descriptor. 61 | */ 62 | void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { 63 | if (m_handle != NULL_HANDLE) { 64 | ESP_LOGE(LOG_TAG, "Descriptor already has a handle."); 65 | return; 66 | } 67 | 68 | m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. 69 | 70 | esp_attr_control_t control; 71 | control.auto_rsp = ESP_GATT_RSP_BY_APP; 72 | m_semaphoreCreateEvt.take("executeCreate"); 73 | esp_err_t errRc = ::esp_ble_gatts_add_char_descr( 74 | pCharacteristic->getService()->getHandle(), 75 | getUUID().getNative(), 76 | (esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), 77 | &m_value, 78 | &control); 79 | if (errRc != ESP_OK) { 80 | ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 81 | return; 82 | } 83 | 84 | m_semaphoreCreateEvt.wait("executeCreate"); 85 | ESP_LOGD(LOG_TAG, "<< executeCreate"); 86 | } // executeCreate 87 | 88 | 89 | /** 90 | * @brief Get the length of the value of this descriptor. 91 | * @return The length (in bytes) of the value of this descriptor. 92 | */ 93 | size_t BLEDescriptor::getLength() { 94 | return m_value.attr_len; 95 | } // getLength 96 | 97 | 98 | /** 99 | * @brief Get the UUID of the descriptor. 100 | */ 101 | BLEUUID BLEDescriptor::getUUID() { 102 | return m_bleUUID; 103 | } // getUUID 104 | 105 | 106 | 107 | /** 108 | * @brief Get the value of this descriptor. 109 | * @return A pointer to the value of this descriptor. 110 | */ 111 | uint8_t* BLEDescriptor::getValue() { 112 | return m_value.attr_value; 113 | } // getValue 114 | 115 | 116 | /** 117 | * @brief Handle GATT server events for the descripttor. 118 | * @param [in] event 119 | * @param [in] gatts_if 120 | * @param [in] param 121 | */ 122 | void BLEDescriptor::handleGATTServerEvent( 123 | esp_gatts_cb_event_t event, 124 | esp_gatt_if_t gatts_if, 125 | esp_ble_gatts_cb_param_t *param) { 126 | switch(event) { 127 | // ESP_GATTS_ADD_CHAR_DESCR_EVT 128 | // 129 | // add_char_descr: 130 | // - esp_gatt_status_t status 131 | // - uint16_t attr_handle 132 | // - uint16_t service_handle 133 | // - esp_bt_uuid_t char_uuid 134 | case ESP_GATTS_ADD_CHAR_DESCR_EVT: { 135 | /* 136 | ESP_LOGD(LOG_TAG, "DEBUG: m_pCharacteristic: %x", (uint32_t)m_pCharacteristic); 137 | ESP_LOGD(LOG_TAG, "DEBUG: m_bleUUID: %s, add_char_descr.char_uuid: %s, equals: %d", 138 | m_bleUUID.toString().c_str(), 139 | BLEUUID(param->add_char_descr.char_uuid).toString().c_str(), 140 | m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid))); 141 | ESP_LOGD(LOG_TAG, "DEBUG: service->getHandle: %x, add_char_descr.service_handle: %x", 142 | m_pCharacteristic->getService()->getHandle(), param->add_char_descr.service_handle); 143 | ESP_LOGD(LOG_TAG, "DEBUG: service->lastCharacteristic: %x", 144 | (uint32_t)m_pCharacteristic->getService()->getLastCreatedCharacteristic()); 145 | */ 146 | if (m_pCharacteristic != nullptr && 147 | m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) && 148 | m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && 149 | m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { 150 | setHandle(param->add_char_descr.attr_handle); 151 | m_semaphoreCreateEvt.give(); 152 | } 153 | break; 154 | } // ESP_GATTS_ADD_CHAR_DESCR_EVT 155 | 156 | // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived. 157 | // 158 | // write: 159 | // - uint16_t conn_id 160 | // - uint16_t trans_id 161 | // - esp_bd_addr_t bda 162 | // - uint16_t handle 163 | // - uint16_t offset 164 | // - bool need_rsp 165 | // - bool is_prep 166 | // - uint16_t len 167 | // - uint8_t *value 168 | case ESP_GATTS_WRITE_EVT: { 169 | if (param->write.handle == m_handle) { 170 | setValue(param->write.value, param->write.len); // Set the value of the descriptor. 171 | 172 | esp_gatt_rsp_t rsp; // Build a response. 173 | rsp.attr_value.len = getLength(); 174 | rsp.attr_value.handle = m_handle; 175 | rsp.attr_value.offset = 0; 176 | rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 177 | memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); 178 | esp_err_t errRc = ::esp_ble_gatts_send_response( 179 | gatts_if, 180 | param->write.conn_id, 181 | param->write.trans_id, 182 | ESP_GATT_OK, 183 | &rsp); 184 | 185 | if (errRc != ESP_OK) { // Check the return code from the send of the response. 186 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 187 | } 188 | 189 | if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. 190 | m_pCallback->onWrite(this); // Invoke the onWrite callback handler. 191 | } 192 | } // End of ... this is our handle. 193 | 194 | break; 195 | } // ESP_GATTS_WRITE_EVT 196 | 197 | // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived. 198 | // 199 | // read: 200 | // - uint16_t conn_id 201 | // - uint32_t trans_id 202 | // - esp_bd_addr_t bda 203 | // - uint16_t handle 204 | // - uint16_t offset 205 | // - bool is_long 206 | // - bool need_rsp 207 | // 208 | case ESP_GATTS_READ_EVT: { 209 | if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it 210 | 211 | if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now. 212 | m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. 213 | } 214 | 215 | if (param->read.need_rsp) { // Do we need a response 216 | ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); 217 | esp_gatt_rsp_t rsp; 218 | rsp.attr_value.len = getLength(); 219 | rsp.attr_value.handle = param->read.handle; 220 | rsp.attr_value.offset = 0; 221 | rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 222 | memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); 223 | 224 | esp_err_t errRc = ::esp_ble_gatts_send_response( 225 | gatts_if, 226 | param->read.conn_id, 227 | param->read.trans_id, 228 | ESP_GATT_OK, 229 | &rsp); 230 | 231 | if (errRc != ESP_OK) { // Check the return code from the send of the response. 232 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 233 | } 234 | } // End of need a response. 235 | } // End of this is our handle 236 | break; 237 | } // ESP_GATTS_READ_EVT 238 | 239 | default: { 240 | break; 241 | } 242 | }// switch event 243 | } // handleGATTServerEvent 244 | 245 | /** 246 | * @brief Set the handle of this descriptor. 247 | * Set the handle of this descriptor to be the supplied value. 248 | * @param [in] handle The handle to be associated with this descriptor. 249 | * @return N/A. 250 | */ 251 | void BLEDescriptor::setHandle(uint16_t handle) { 252 | ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); 253 | m_handle = handle; 254 | ESP_LOGD(LOG_TAG, "<< setHandle()"); 255 | } // setHandle 256 | 257 | 258 | /** 259 | * @brief Set the value of the descriptor. 260 | * @param [in] data The data to set for the descriptor. 261 | * @param [in] length The length of the data in bytes. 262 | */ 263 | void BLEDescriptor::setValue(uint8_t* data, size_t length) { 264 | if (length > ESP_GATT_MAX_ATTR_LEN) { 265 | ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); 266 | return; 267 | } 268 | m_value.attr_len = length; 269 | memcpy(m_value.attr_value, data, length); 270 | } // setValue 271 | 272 | 273 | /** 274 | * @brief Set the value of the descriptor. 275 | * @param [in] value The value of the descriptor in string form. 276 | */ 277 | void BLEDescriptor::setValue(std::string value) { 278 | setValue((uint8_t *)value.data(), value.length()); 279 | } // setValue 280 | BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} 281 | 282 | /** 283 | * @brief Callback function to support a read request. 284 | * @param [in] pDescriptor The descriptor that is the source of the event. 285 | */ 286 | void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) { 287 | ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default"); 288 | ESP_LOGD("BLEDescriptorCallbacks", "<< onRead"); 289 | } // onRead 290 | 291 | 292 | /** 293 | * @brief Callback function to support a write request. 294 | * @param [in] pDescriptor The descriptor that is the source of the event. 295 | */ 296 | void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) { 297 | ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default"); 298 | ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite"); 299 | } // onWrite 300 | 301 | 302 | #endif /* CONFIG_BT_ENABLED */ 303 | -------------------------------------------------------------------------------- /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); 28 | BLEDescriptor(BLEUUID uuid); 29 | virtual ~BLEDescriptor(); 30 | 31 | size_t getLength(); // Get the length of the value of the descriptor. 32 | BLEUUID getUUID(); // Get the UUID of the descriptor. 33 | uint8_t* getValue(); // Get a pointer to the value of the descriptor. 34 | void handleGATTServerEvent( 35 | esp_gatts_cb_event_t event, 36 | esp_gatt_if_t gatts_if, 37 | esp_ble_gatts_cb_param_t* param); 38 | void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. 39 | void setValue(std::string value); // Set the value of the descriptor as a data buffer. 40 | private: 41 | friend class BLEDescriptorMap; 42 | friend class BLECharacteristic; 43 | BLEUUID m_bleUUID; 44 | uint16_t m_handle; 45 | BLEDescriptorCallbacks* m_pCallback; 46 | BLECharacteristic* m_pCharacteristic; 47 | esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; 48 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 49 | esp_attr_value_t m_value; 50 | 51 | void executeCreate(BLECharacteristic* pCharacteristic); 52 | void setHandle(uint16_t handle); 53 | }; // BLEDescriptor 54 | 55 | 56 | /** 57 | * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. 58 | * 59 | * When a server application creates a %BLE descriptor, we may wish to be informed when there is either 60 | * a read or write request to the descriptors value. An application can register a 61 | * sub-classed instance of this class and will be notified when such an event happens. 62 | */ 63 | class BLEDescriptorCallbacks { 64 | public: 65 | virtual ~BLEDescriptorCallbacks(); 66 | virtual void onRead(BLEDescriptor* pDescriptor); 67 | virtual void onWrite(BLEDescriptor* pDescriptor); 68 | }; 69 | #endif /* CONFIG_BT_ENABLED */ 70 | #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ 71 | -------------------------------------------------------------------------------- /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.second->getUUID().equals(uuid)) { 36 | return myPair.second; 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(uuid, pDescriptor)); 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(uuid.toString(), pDescriptor)); 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, 84 | BLEDescriptor *pDescriptor) { 85 | m_handleMap.insert(std::pair(handle, pDescriptor)); 86 | } // setByHandle 87 | 88 | 89 | /** 90 | * @brief Return a string representation of the descriptor map. 91 | * @return A string representation of the descriptor map. 92 | */ 93 | std::string BLEDescriptorMap::toString() { 94 | return "-"; 95 | } // toString 96 | 97 | 98 | /** 99 | * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping 100 | * @param [in] event 101 | * @param [in] gatts_if 102 | * @param [in] param 103 | */ 104 | void BLEDescriptorMap::handleGATTServerEvent( 105 | esp_gatts_cb_event_t event, 106 | esp_gatt_if_t gatts_if, 107 | esp_ble_gatts_cb_param_t *param) { 108 | // Invoke the handler for every descriptor we have. 109 | for (auto &myPair : m_uuidMap) { 110 | myPair.second->handleGATTServerEvent(event, gatts_if, param); 111 | } 112 | } // handleGATTServerEvent 113 | 114 | 115 | /** 116 | * @brief Get the first descriptor in the map. 117 | * @return The first descriptor in the map. 118 | */ 119 | BLEDescriptor* BLEDescriptorMap::getFirst() { 120 | m_iterator = m_uuidMap.begin(); 121 | if (m_iterator == m_uuidMap.end()) { 122 | return nullptr; 123 | } 124 | BLEDescriptor *pRet = m_iterator->second; 125 | m_iterator++; 126 | return pRet; 127 | } // getFirst 128 | 129 | 130 | /** 131 | * @brief Get the next descriptor in the map. 132 | * @return The next descriptor in the map. 133 | */ 134 | BLEDescriptor* BLEDescriptorMap::getNext() { 135 | if (m_iterator == m_uuidMap.end()) { 136 | return nullptr; 137 | } 138 | BLEDescriptor *pRet = m_iterator->second; 139 | m_iterator++; 140 | return pRet; 141 | } // getNext 142 | #endif /* CONFIG_BT_ENABLED */ 143 | -------------------------------------------------------------------------------- /src/BLEDevice.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLE.cpp 3 | * 4 | * Created on: Mar 16, 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 // ESP32 BLE 15 | #include // ESP32 BLE 16 | #include // ESP32 BLE 17 | #include // ESP32 BLE 18 | #include // ESP32 BLE 19 | #include // ESP32 BLE 20 | #include // ESP32 BLE 21 | #include // ESP32 ESP-IDF 22 | #include // ESP32 ESP-IDF 23 | #include // Part of C++ Standard library 24 | #include // Part of C++ Standard library 25 | #include // Part of C++ Standard library 26 | 27 | #include "BLEDevice.h" 28 | #include "GeneralUtils.h" 29 | #ifdef ARDUINO_ARCH_ESP32 30 | #include "esp32-hal-log.h" 31 | #endif 32 | 33 | static const char* LOG_TAG = "BLEDevice"; 34 | 35 | /** 36 | * Singletons for the BLEDevice. 37 | */ 38 | BLEServer* BLEDevice::m_pServer = nullptr; 39 | bool initialized = false; // Have we been initialized? 40 | esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; 41 | uint16_t BLEDevice::m_localMTU = 23; 42 | 43 | 44 | /** 45 | * @brief Create a new instance of a server. 46 | * @return A new instance of the server. 47 | */ 48 | /* STATIC */ BLEServer* BLEDevice::createServer() { 49 | ESP_LOGD(LOG_TAG, ">> createServer"); 50 | #ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig 51 | ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); 52 | abort(); 53 | #endif // CONFIG_GATTS_ENABLE 54 | m_pServer = new BLEServer(); 55 | m_pServer->createApp(0); 56 | ESP_LOGD(LOG_TAG, "<< createServer"); 57 | return m_pServer; 58 | } // createServer 59 | 60 | 61 | /** 62 | * @brief Handle GATT server events. 63 | * 64 | * @param [in] event The event that has been newly received. 65 | * @param [in] gatts_if The connection to the GATT interface. 66 | * @param [in] param Parameters for the event. 67 | */ 68 | /* STATIC */ void BLEDevice::gattServerEventHandler( 69 | esp_gatts_cb_event_t event, 70 | esp_gatt_if_t gatts_if, 71 | esp_ble_gatts_cb_param_t* param 72 | ) { 73 | 74 | switch(event) { 75 | case ESP_GATTS_CONNECT_EVT: { 76 | BLEDevice::m_localMTU = 23; 77 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 78 | if(BLEDevice::m_securityLevel){ 79 | esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); 80 | } 81 | #endif // CONFIG_BLE_SMP_ENABLE 82 | break; 83 | } // ESP_GATTS_CONNECT_EVT 84 | 85 | case ESP_GATTS_MTU_EVT: { 86 | BLEDevice::m_localMTU = param->mtu.mtu; 87 | ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); 88 | break; 89 | } 90 | default: { 91 | break; 92 | } 93 | } // switch 94 | 95 | 96 | if (BLEDevice::m_pServer != nullptr) { 97 | BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); 98 | } 99 | } // gattServerEventHandler 100 | 101 | 102 | /** 103 | * @brief Handle GAP events. 104 | */ 105 | /* STATIC */ void BLEDevice::gapEventHandler( 106 | esp_gap_ble_cb_event_t event, 107 | esp_ble_gap_cb_param_t *param) { 108 | 109 | switch(event) { 110 | 111 | case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ 112 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); 113 | break; 114 | case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ 115 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); 116 | break; 117 | case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ 118 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); 119 | break; 120 | case ESP_GAP_BLE_NC_REQ_EVT: 121 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); 122 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 123 | if(BLEDevice::m_securityCallbacks!=nullptr){ 124 | esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); 125 | } 126 | #endif // CONFIG_BLE_SMP_ENABLE 127 | break; 128 | case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ 129 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); 130 | // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); 131 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 132 | if(BLEDevice::m_securityCallbacks!=nullptr){ 133 | esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); 134 | } 135 | #endif // CONFIG_BLE_SMP_ENABLE 136 | break; 137 | /* 138 | * TODO should we add white/black list comparison? 139 | */ 140 | case ESP_GAP_BLE_SEC_REQ_EVT: 141 | /* send the positive(true) security response to the peer device to accept the security request. 142 | If not accept the security request, should sent the security response with negative(false) accept value*/ 143 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); 144 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 145 | if(BLEDevice::m_securityCallbacks!=nullptr){ 146 | esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); 147 | } 148 | else{ 149 | esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); 150 | } 151 | #endif // CONFIG_BLE_SMP_ENABLE 152 | break; 153 | /* 154 | * 155 | */ 156 | case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. 157 | ///show the passkey number to the user to input it in the peer deivce. 158 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); 159 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 160 | if(BLEDevice::m_securityCallbacks!=nullptr){ 161 | ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); 162 | BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); 163 | } 164 | #endif // CONFIG_BLE_SMP_ENABLE 165 | break; 166 | case ESP_GAP_BLE_KEY_EVT: 167 | //shows the ble key type info share with peer device to the user. 168 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); 169 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 170 | ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); 171 | #endif // CONFIG_BLE_SMP_ENABLE 172 | break; 173 | case ESP_GAP_BLE_AUTH_CMPL_EVT: 174 | ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); 175 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 176 | if(BLEDevice::m_securityCallbacks!=nullptr){ 177 | BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); 178 | } 179 | #endif // CONFIG_BLE_SMP_ENABLE 180 | break; 181 | default: { 182 | break; 183 | } 184 | } // switch 185 | 186 | if (BLEDevice::m_pServer != nullptr) { 187 | BLEDevice::m_pServer->handleGAPEvent(event, param); 188 | } 189 | 190 | /* 191 | * Security events: 192 | */ 193 | 194 | 195 | } // gapEventHandler 196 | 197 | 198 | 199 | /** 200 | * @brief Initialize the %BLE environment. 201 | * @param deviceName The device name of the device. 202 | */ 203 | /* STATIC */ void BLEDevice::init(std::string deviceName) { 204 | if(!initialized){ 205 | initialized = true; // Set the initialization flag to ensure we are only initialized once. 206 | 207 | esp_err_t errRc = ::nvs_flash_init(); 208 | if (errRc != ESP_OK) { 209 | ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 210 | return; 211 | } 212 | 213 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 214 | errRc = esp_bt_controller_init(&bt_cfg); 215 | if (errRc != ESP_OK) { 216 | ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 217 | return; 218 | } 219 | 220 | #ifndef CLASSIC_BT_ENABLED 221 | // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue 222 | errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); 223 | //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); 224 | if (errRc != ESP_OK) { 225 | ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 226 | return; 227 | } 228 | #else 229 | errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); 230 | if (errRc != ESP_OK) { 231 | ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 232 | return; 233 | } 234 | #endif 235 | 236 | errRc = esp_bluedroid_init(); 237 | if (errRc != ESP_OK) { 238 | ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 239 | return; 240 | } 241 | 242 | errRc = esp_bluedroid_enable(); 243 | if (errRc != ESP_OK) { 244 | ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 245 | return; 246 | } 247 | 248 | errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); 249 | if (errRc != ESP_OK) { 250 | ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 251 | return; 252 | } 253 | 254 | #ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig 255 | errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); 256 | if (errRc != ESP_OK) { 257 | ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 258 | return; 259 | } 260 | #endif // CONFIG_GATTC_ENABLE 261 | 262 | #ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig 263 | errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); 264 | if (errRc != ESP_OK) { 265 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 266 | return; 267 | } 268 | #endif // CONFIG_GATTS_ENABLE 269 | 270 | errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); 271 | if (errRc != ESP_OK) { 272 | ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 273 | return; 274 | }; 275 | 276 | #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig 277 | esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; 278 | errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); 279 | if (errRc != ESP_OK) { 280 | ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 281 | return; 282 | }; 283 | #endif // CONFIG_BLE_SMP_ENABLE 284 | } 285 | vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. 286 | } // init 287 | 288 | 289 | 290 | /* 291 | * @brief Get local MTU value set during mtu request or default value 292 | */ 293 | uint16_t BLEDevice::getMTU() { 294 | return m_localMTU; 295 | } 296 | #endif // CONFIG_BT_ENABLED 297 | -------------------------------------------------------------------------------- /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 | 20 | /** 21 | * @brief %BLE functions. 22 | */ 23 | class BLEDevice { 24 | public: 25 | 26 | static BLEServer* createServer(); // Cretae a new BLE server. 27 | static void init(std::string deviceName); // Initialize the local BLE environment. 28 | static uint16_t getMTU(); 29 | 30 | private: 31 | static BLEServer *m_pServer; 32 | static esp_ble_sec_act_t m_securityLevel; 33 | static uint16_t m_localMTU; 34 | 35 | static esp_gatt_if_t getGattcIF(); 36 | 37 | static void gattServerEventHandler( 38 | esp_gatts_cb_event_t event, 39 | esp_gatt_if_t gatts_if, 40 | esp_ble_gatts_cb_param_t* param); 41 | 42 | static void gapEventHandler( 43 | esp_gap_ble_cb_event_t event, 44 | esp_ble_gap_cb_param_t* param); 45 | 46 | }; // class BLE 47 | 48 | #endif // CONFIG_BT_ENABLED 49 | #endif /* MAIN_BLEDevice_H_ */ 50 | -------------------------------------------------------------------------------- /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 13 | #include 14 | //#include 15 | #include "BLEDevice.h" 16 | #include "BLEServer.h" 17 | #include "BLEService.h" 18 | #include 19 | #include 20 | // #include 21 | #include 22 | #ifdef ARDUINO_ARCH_ESP32 23 | #include "esp32-hal-log.h" 24 | #endif 25 | 26 | static const char* LOG_TAG = "BLEServer"; 27 | 28 | 29 | /** 30 | * @brief Construct a %BLE Server 31 | * 32 | * This class is not designed to be individually instantiated. Instead one should create a server by asking 33 | * the BLEDevice class. 34 | */ 35 | BLEServer::BLEServer() { 36 | m_appId = -1; 37 | m_gatts_if = -1; 38 | m_connectedCount = 0; 39 | m_connId = -1; 40 | m_pServerCallbacks = nullptr; 41 | 42 | //createApp(0); 43 | } // BLEServer 44 | 45 | 46 | void BLEServer::createApp(uint16_t appId) { 47 | m_appId = appId; 48 | registerApp(); 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 | * @return A reference to the new service object. 73 | */ 74 | BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { 75 | ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); 76 | m_semaphoreCreateEvt.take("createService"); 77 | 78 | // Check that a service with the supplied UUID does not already exist. 79 | if (m_serviceMap.getByUUID(uuid) != nullptr) { 80 | ESP_LOGE(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", 81 | uuid.toString().c_str()); 82 | m_semaphoreCreateEvt.give(); 83 | return nullptr; 84 | } 85 | 86 | BLEService* pService = new BLEService(uuid, numHandles); 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 Retrieve the advertising object that can be used to advertise the existence of the server. 99 | * 100 | * @return An advertising object. 101 | */ 102 | BLEAdvertising* BLEServer::getAdvertising() { 103 | return &m_bleAdvertising; 104 | } 105 | 106 | uint16_t BLEServer::getConnId() { 107 | return m_connId; 108 | } 109 | 110 | 111 | 112 | uint16_t BLEServer::getGattsIf() { 113 | return m_gatts_if; 114 | } 115 | 116 | /** 117 | * @brief Handle a received GAP event. 118 | * 119 | * @param [in] event 120 | * @param [in] param 121 | */ 122 | void BLEServer::handleGAPEvent( 123 | esp_gap_ble_cb_event_t event, 124 | esp_ble_gap_cb_param_t* param) { 125 | ESP_LOGD(LOG_TAG, "BLEServer ... handling GAP event!"); 126 | switch(event) { 127 | case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { 128 | /* 129 | esp_ble_adv_params_t adv_params; 130 | adv_params.adv_int_min = 0x20; 131 | adv_params.adv_int_max = 0x40; 132 | adv_params.adv_type = ADV_TYPE_IND; 133 | adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; 134 | adv_params.channel_map = ADV_CHNL_ALL; 135 | adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; 136 | ESP_LOGD(tag, "Starting advertising"); 137 | esp_err_t errRc = ::esp_ble_gap_start_advertising(&adv_params); 138 | if (errRc != ESP_OK) { 139 | ESP_LOGE(tag, "esp_ble_gap_start_advertising: rc=%d %s", errRc, espToString(errRc)); 140 | return; 141 | } 142 | */ 143 | break; 144 | } 145 | 146 | default: 147 | break; 148 | } 149 | } // handleGAPEvent 150 | 151 | 152 | 153 | /** 154 | * @brief Handle a GATT Server Event. 155 | * 156 | * @param [in] event 157 | * @param [in] gatts_if 158 | * @param [in] param 159 | * 160 | */ 161 | void BLEServer::handleGATTServerEvent( 162 | esp_gatts_cb_event_t event, 163 | esp_gatt_if_t gatts_if, 164 | esp_ble_gatts_cb_param_t* param) { 165 | 166 | // Invoke the handler for every Service we have. 167 | m_serviceMap.handleGATTServerEvent(event, gatts_if, param); 168 | 169 | switch(event) { 170 | // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. 171 | // add_char: 172 | // - esp_gatt_status_t status 173 | // - uint16_t attr_handle 174 | // - uint16_t service_handle 175 | // - esp_bt_uuid_t char_uuid 176 | // 177 | case ESP_GATTS_ADD_CHAR_EVT: { 178 | break; 179 | } // ESP_GATTS_ADD_CHAR_EVT 180 | 181 | 182 | // ESP_GATTS_CONNECT_EVT 183 | // connect: 184 | // - uint16_t conn_id 185 | // - esp_bd_addr_t remote_bda 186 | // - bool is_connected 187 | // 188 | case ESP_GATTS_CONNECT_EVT: { 189 | m_connId = param->connect.conn_id; // Save the connection id. 190 | if (m_pServerCallbacks != nullptr) { 191 | m_pServerCallbacks->onConnect(this); 192 | } 193 | m_connectedCount++; // Increment the number of connected devices count. 194 | break; 195 | } // ESP_GATTS_CONNECT_EVT 196 | 197 | 198 | // ESP_GATTS_CREATE_EVT 199 | // Called when a new service is registered as having been created. 200 | // 201 | // create: 202 | // * esp_gatt_status_t status 203 | // * uint16_t service_handle 204 | // * esp_gatt_srvc_id_t service_id 205 | // 206 | case ESP_GATTS_CREATE_EVT: { 207 | BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid); 208 | m_serviceMap.setByHandle(param->create.service_handle, pService); 209 | m_semaphoreCreateEvt.give(); 210 | break; 211 | } // ESP_GATTS_CREATE_EVT 212 | 213 | 214 | // ESP_GATTS_DISCONNECT_EVT 215 | // 216 | // disconnect 217 | // - uint16_t conn_id 218 | // - esp_bd_addr_t remote_bda 219 | // - bool is_connected 220 | // 221 | // If we receive a disconnect event then invoke the callback for disconnects (if one is present). 222 | // we also want to start advertising again. 223 | case ESP_GATTS_DISCONNECT_EVT: { 224 | m_connectedCount--; // Decrement the number of connected devices count. 225 | if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. 226 | m_pServerCallbacks->onDisconnect(this); 227 | } 228 | startAdvertising(); //- do this with some delay from the loop() 229 | break; 230 | } // ESP_GATTS_DISCONNECT_EVT 231 | 232 | 233 | // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. 234 | // 235 | // read: 236 | // - uint16_t conn_id 237 | // - uint32_t trans_id 238 | // - esp_bd_addr_t bda 239 | // - uint16_t handle 240 | // - uint16_t offset 241 | // - bool is_long 242 | // - bool need_rsp 243 | // 244 | case ESP_GATTS_READ_EVT: { 245 | break; 246 | } // ESP_GATTS_READ_EVT 247 | 248 | 249 | // ESP_GATTS_REG_EVT 250 | // reg: 251 | // - esp_gatt_status_t status 252 | // - uint16_t app_id 253 | // 254 | case ESP_GATTS_REG_EVT: { 255 | m_gatts_if = gatts_if; 256 | m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. 257 | break; 258 | } // ESP_GATTS_REG_EVT 259 | 260 | 261 | // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. 262 | // 263 | // write: 264 | // - uint16_t conn_id 265 | // - uint16_t trans_id 266 | // - esp_bd_addr_t bda 267 | // - uint16_t handle 268 | // - uint16_t offset 269 | // - bool need_rsp 270 | // - bool is_prep 271 | // - uint16_t len 272 | // - uint8_t* value 273 | // 274 | case ESP_GATTS_WRITE_EVT: { 275 | break; 276 | } 277 | 278 | default: { 279 | break; 280 | } 281 | } 282 | ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); 283 | } // handleGATTServerEvent 284 | 285 | 286 | /** 287 | * @brief Register the app. 288 | * 289 | * @return N/A 290 | */ 291 | void BLEServer::registerApp() { 292 | ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); 293 | m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. 294 | ::esp_ble_gatts_app_register(m_appId); 295 | m_semaphoreRegisterAppEvt.wait("registerApp"); 296 | ESP_LOGD(LOG_TAG, "<< registerApp"); 297 | } // registerApp 298 | 299 | 300 | /** 301 | * @brief Set the server callbacks. 302 | * 303 | * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client 304 | * disconnecting. This function can be called to register a callback handler that will be invoked when these 305 | * events are detected. 306 | * 307 | * @param [in] pCallbacks The callbacks to be invoked. 308 | */ 309 | void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { 310 | m_pServerCallbacks = pCallbacks; 311 | } // setCallbacks 312 | 313 | 314 | /** 315 | * @brief Start advertising. 316 | * 317 | * Start the server advertising its existence. This is a convenience function and is equivalent to 318 | * retrieving the advertising object and invoking start upon it. 319 | */ 320 | void BLEServer::startAdvertising() { 321 | ESP_LOGD(LOG_TAG, ">> startAdvertising"); 322 | m_bleAdvertising.start(); 323 | ESP_LOGD(LOG_TAG, "<< startAdvertising"); 324 | } // startAdvertising 325 | 326 | 327 | void BLEServerCallbacks::onConnect(BLEServer* pServer) { 328 | ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); 329 | ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); 330 | } // onConnect 331 | 332 | 333 | void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { 334 | ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); 335 | ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); 336 | } // onDisconnect 337 | 338 | #endif // CONFIG_BT_ENABLED 339 | -------------------------------------------------------------------------------- /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 | 17 | #include "BLEUUID.h" 18 | #include "BLEAdvertising.h" 19 | #include "BLECharacteristic.h" 20 | #include "BLEService.h" 21 | #include "FreeRTOS.h" 22 | 23 | class BLEServerCallbacks; 24 | 25 | 26 | /** 27 | * @brief A data structure that manages the %BLE servers owned by a BLE server. 28 | */ 29 | class BLEServiceMap { 30 | public: 31 | BLEService* getByHandle(uint16_t handle); 32 | BLEService* getByUUID(const char* uuid); 33 | BLEService* getByUUID(BLEUUID uuid); 34 | void handleGATTServerEvent( 35 | esp_gatts_cb_event_t event, 36 | esp_gatt_if_t gatts_if, 37 | esp_ble_gatts_cb_param_t* param); 38 | void setByHandle(uint16_t handle, BLEService* service); 39 | void setByUUID(const char* uuid, BLEService* service); 40 | void setByUUID(BLEUUID uuid, BLEService* service); 41 | std::string toString(); 42 | 43 | private: 44 | std::map m_uuidMap; 45 | std::map m_handleMap; 46 | }; 47 | 48 | 49 | /** 50 | * @brief The model of a %BLE server. 51 | */ 52 | class BLEServer { 53 | public: 54 | BLEService* createService(const char* uuid); 55 | BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); 56 | BLEAdvertising* getAdvertising(); 57 | void setCallbacks(BLEServerCallbacks* pCallbacks); 58 | void startAdvertising(); 59 | 60 | 61 | private: 62 | BLEServer(); 63 | friend class BLEService; 64 | friend class BLECharacteristic; 65 | friend class BLEDevice; 66 | esp_ble_adv_data_t m_adv_data; 67 | uint16_t m_appId; 68 | BLEAdvertising m_bleAdvertising; 69 | uint16_t m_connId; 70 | uint32_t m_connectedCount; 71 | uint16_t m_gatts_if; 72 | FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); 73 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 74 | BLEServiceMap m_serviceMap; 75 | BLEServerCallbacks* m_pServerCallbacks; 76 | 77 | void createApp(uint16_t appId); 78 | uint16_t getConnId(); 79 | uint16_t getGattsIf(); 80 | void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); 81 | void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); 82 | void registerApp(); 83 | }; // BLEServer 84 | 85 | 86 | /** 87 | * @brief Callbacks associated with the operation of a %BLE server. 88 | */ 89 | class BLEServerCallbacks { 90 | public: 91 | virtual ~BLEServerCallbacks() {}; 92 | /** 93 | * @brief Handle a new client connection. 94 | * 95 | * When a new client connects, we are invoked. 96 | * 97 | * @param [in] pServer A reference to the %BLE server that received the client connection. 98 | */ 99 | virtual void onConnect(BLEServer* pServer); 100 | 101 | /** 102 | * @brief Handle an existing client disconnection. 103 | * 104 | * When an existing client disconnects, we are invoked. 105 | * 106 | * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. 107 | */ 108 | virtual void onDisconnect(BLEServer* pServer); 109 | }; // BLEServerCallbacks 110 | 111 | 112 | 113 | #endif /* CONFIG_BT_ENABLED */ 114 | #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ 115 | -------------------------------------------------------------------------------- /src/BLEService.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * BLEService.cpp 3 | * 4 | * Created on: Mar 25, 2017 5 | * Author: kolban 6 | */ 7 | 8 | // A service is identified by a UUID. A service is also the container for one or more characteristics. 9 | 10 | #include "sdkconfig.h" 11 | #if defined(CONFIG_BT_ENABLED) 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "BLEServer.h" 21 | #include "BLEService.h" 22 | #include "GeneralUtils.h" 23 | 24 | #ifdef ARDUINO_ARCH_ESP32 25 | #include "esp32-hal-log.h" 26 | #endif 27 | 28 | #define NULL_HANDLE (0xffff) 29 | 30 | static const char* LOG_TAG = "BLEService"; // Tag for logging. 31 | 32 | /** 33 | * @brief Construct an instance of the BLEService 34 | * @param [in] uuid The UUID of the service. 35 | * @param [in] numHandles The maximum number of handles associated with the service. 36 | */ 37 | BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { 38 | } 39 | 40 | 41 | /** 42 | * @brief Construct an instance of the BLEService 43 | * @param [in] uuid The UUID of the service. 44 | * @param [in] numHandles The maximum number of handles associated with the service. 45 | */ 46 | BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { 47 | m_uuid = uuid; 48 | m_handle = NULL_HANDLE; 49 | m_pServer = nullptr; 50 | //m_serializeMutex.setName("BLEService"); 51 | m_lastCreatedCharacteristic = nullptr; 52 | m_numHandles = numHandles; 53 | } // BLEService 54 | 55 | 56 | /** 57 | * @brief Create the service. 58 | * Create the service. 59 | * @param [in] gatts_if The handle of the GATT server interface. 60 | * @return N/A. 61 | */ 62 | 63 | void BLEService::executeCreate(BLEServer *pServer) { 64 | //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); 65 | //getUUID(); // Needed for a weird bug fix 66 | //char x[1000]; 67 | //memcpy(x, &m_uuid, sizeof(m_uuid)); 68 | //char x[10]; 69 | //memcpy(x, &deleteMe, 10); 70 | m_pServer = pServer; 71 | m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT 72 | 73 | esp_gatt_srvc_id_t srvc_id; 74 | srvc_id.is_primary = true; 75 | srvc_id.id.inst_id = 0; 76 | srvc_id.id.uuid = *m_uuid.getNative(); 77 | esp_err_t errRc = ::esp_ble_gatts_create_service( 78 | getServer()->getGattsIf(), 79 | &srvc_id, 80 | m_numHandles // The maximum number of handles associated with the service. 81 | ); 82 | 83 | if (errRc != ESP_OK) { 84 | ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 85 | return; 86 | } 87 | 88 | m_semaphoreCreateEvt.wait("executeCreate"); 89 | ESP_LOGD(LOG_TAG, "<< executeCreate"); 90 | } // executeCreate 91 | 92 | /** 93 | * @brief Get the UUID of the service. 94 | * @return the UUID of the service. 95 | */ 96 | BLEUUID BLEService::getUUID() { 97 | return m_uuid; 98 | } // getUUID 99 | 100 | /** 101 | * @brief Start the service. 102 | * Here we wish to start the service which means that we will respond to partner requests about it. 103 | * Starting a service also means that we can create the corresponding characteristics. 104 | * @return Start the service. 105 | */ 106 | void BLEService::start() { 107 | // We ask the BLE runtime to start the service and then create each of the characteristics. 108 | // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event 109 | // obtained as a result of calling esp_ble_gatts_create_service(). 110 | // 111 | if (m_handle == NULL_HANDLE) { 112 | ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); 113 | return; 114 | } 115 | 116 | 117 | BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); 118 | 119 | while(pCharacteristic != nullptr) { 120 | m_lastCreatedCharacteristic = pCharacteristic; 121 | pCharacteristic->executeCreate(this); 122 | 123 | pCharacteristic = m_characteristicMap.getNext(); 124 | } 125 | // Start each of the characteristics ... these are found in the m_characteristicMap. 126 | 127 | m_semaphoreStartEvt.take("start"); 128 | esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); 129 | 130 | if (errRc != ESP_OK) { 131 | ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); 132 | return; 133 | } 134 | m_semaphoreStartEvt.wait("start"); 135 | 136 | ESP_LOGD(LOG_TAG, "<< start()"); 137 | } // start 138 | 139 | 140 | /** 141 | * @brief Set the handle associated with this service. 142 | * @param [in] handle The handle associated with the service. 143 | */ 144 | void BLEService::setHandle(uint16_t handle) { 145 | if (m_handle != NULL_HANDLE) { 146 | ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); 147 | return; 148 | } 149 | m_handle = handle; 150 | ESP_LOGD(LOG_TAG, "<< setHandle"); 151 | } // setHandle 152 | 153 | 154 | /** 155 | * @brief Get the handle associated with this service. 156 | * @return The handle associated with this service. 157 | */ 158 | uint16_t BLEService::getHandle() { 159 | return m_handle; 160 | } // getHandle 161 | 162 | 163 | /** 164 | * @brief Add a characteristic to the service. 165 | * @param [in] pCharacteristic A pointer to the characteristic to be added. 166 | */ 167 | void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { 168 | // We maintain a mapping of characteristics owned by this service. These are managed by the 169 | // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic 170 | // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). 171 | // 172 | ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); 173 | 174 | // Check that we don't add the same characteristic twice. 175 | if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { 176 | ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); 177 | //return; 178 | } 179 | 180 | // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID 181 | // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. 182 | m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); 183 | 184 | ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); 185 | } // addCharacteristic 186 | 187 | 188 | /** 189 | * @brief Create a new BLE Characteristic associated with this service. 190 | * @param [in] uuid - The UUID of the characteristic. 191 | * @param [in] properties - The properties of the characteristic. 192 | * @return The new BLE characteristic. 193 | */ 194 | BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { 195 | return createCharacteristic(BLEUUID(uuid), properties); 196 | } 197 | 198 | 199 | /** 200 | * @brief Create a new BLE Characteristic associated with this service. 201 | * @param [in] uuid - The UUID of the characteristic. 202 | * @param [in] properties - The properties of the characteristic. 203 | * @return The new BLE characteristic. 204 | */ 205 | BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { 206 | BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); 207 | addCharacteristic(pCharacteristic); 208 | return pCharacteristic; 209 | } // createCharacteristic 210 | 211 | 212 | /** 213 | * @brief Handle a GATTS server event. 214 | */ 215 | void BLEService::handleGATTServerEvent( 216 | esp_gatts_cb_event_t event, 217 | esp_gatt_if_t gatts_if, 218 | esp_ble_gatts_cb_param_t *param) { 219 | 220 | 221 | switch(event) { 222 | // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. 223 | // add_char: 224 | // - esp_gatt_status_t status 225 | // - uint16_t attr_handle 226 | // - uint16_t service_handle 227 | // - esp_bt_uuid_t char_uuid 228 | 229 | // If we have reached the correct service, then locate the characteristic and remember the handle 230 | // for that characteristic. 231 | case ESP_GATTS_ADD_CHAR_EVT: { 232 | if (m_handle == param->add_char.service_handle) { 233 | BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); 234 | if (pCharacteristic == nullptr) { 235 | ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", 236 | BLEUUID(param->add_char.char_uuid).toString().c_str()); 237 | break; 238 | } 239 | pCharacteristic->setHandle(param->add_char.attr_handle); 240 | m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); 241 | //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); 242 | break; 243 | } // Reached the correct service. 244 | break; 245 | } // ESP_GATTS_ADD_CHAR_EVT 246 | 247 | 248 | // ESP_GATTS_START_EVT 249 | // 250 | // start: 251 | // esp_gatt_status_t status 252 | // uint16_t service_handle 253 | case ESP_GATTS_START_EVT: { 254 | if (param->start.service_handle == getHandle()) { 255 | m_semaphoreStartEvt.give(); 256 | } 257 | break; 258 | } // ESP_GATTS_START_EVT 259 | 260 | 261 | // ESP_GATTS_CREATE_EVT 262 | // Called when a new service is registered as having been created. 263 | // 264 | // create: 265 | // * esp_gatt_status_t status 266 | // * uint16_t service_handle 267 | // * esp_gatt_srvc_id_t service_id 268 | // * - esp_gatt_id id 269 | // * - esp_bt_uuid uuid 270 | // * - uint8_t inst_id 271 | // * - bool is_primary 272 | // 273 | case ESP_GATTS_CREATE_EVT: { 274 | if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid))) { 275 | setHandle(param->create.service_handle); 276 | m_semaphoreCreateEvt.give(); 277 | } 278 | break; 279 | } // ESP_GATTS_CREATE_EVT 280 | 281 | default: { 282 | break; 283 | } // Default 284 | } // Switch 285 | 286 | // Invoke the GATTS handler in each of the associated characteristics. 287 | m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); 288 | } // handleGATTServerEvent 289 | 290 | /** 291 | * @brief Get the last created characteristic. 292 | * It is lamentable that this function has to exist. It returns the last created characteristic. 293 | * We need this because the descriptor API is built around the notion that a new descriptor, when created, 294 | * is associated with the last characteristics created and we need that information. 295 | * @return The last created characteristic. 296 | */ 297 | BLECharacteristic* BLEService::getLastCreatedCharacteristic() { 298 | return m_lastCreatedCharacteristic; 299 | } // getLastCreatedCharacteristic 300 | 301 | 302 | /** 303 | * @brief Get the BLE server associated with this service. 304 | * @return The BLEServer associated with this service. 305 | */ 306 | BLEServer* BLEService::getServer() { 307 | return m_pServer; 308 | } // getServer 309 | 310 | #endif // CONFIG_BT_ENABLED 311 | -------------------------------------------------------------------------------- /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( 37 | esp_gatts_cb_event_t event, 38 | esp_gatt_if_t gatts_if, 39 | esp_ble_gatts_cb_param_t* param); 40 | 41 | 42 | private: 43 | std::map m_uuidMap; 44 | std::map m_handleMap; 45 | std::map::iterator m_iterator; 46 | }; 47 | 48 | 49 | /** 50 | * @brief The model of a %BLE service. 51 | * 52 | */ 53 | class BLEService { 54 | public: 55 | void addCharacteristic(BLECharacteristic* pCharacteristic); 56 | BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); 57 | BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); 58 | void executeCreate(BLEServer* pServer); 59 | BLEUUID getUUID(); 60 | BLEServer* getServer(); 61 | void start(); 62 | uint16_t getHandle(); 63 | 64 | private: 65 | BLEService(const char* uuid, uint32_t numHandles); 66 | BLEService(BLEUUID uuid, uint32_t numHandles); 67 | friend class BLEServer; 68 | friend class BLEServiceMap; 69 | friend class BLEDescriptor; 70 | friend class BLECharacteristic; 71 | friend class BLEDevice; 72 | 73 | BLECharacteristicMap m_characteristicMap; 74 | uint16_t m_handle; 75 | BLECharacteristic* m_lastCreatedCharacteristic; 76 | BLEServer* m_pServer; 77 | BLEUUID m_uuid; 78 | 79 | FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); 80 | FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); 81 | 82 | uint32_t m_numHandles; 83 | 84 | BLECharacteristic* getLastCreatedCharacteristic(); 85 | void handleGATTServerEvent( 86 | esp_gatts_cb_event_t event, 87 | esp_gatt_if_t gatts_if, 88 | esp_ble_gatts_cb_param_t* param); 89 | void setHandle(uint16_t handle); 90 | //void setService(esp_gatt_srvc_id_t srvc_id); 91 | }; // BLEService 92 | 93 | 94 | #endif // CONFIG_BT_ENABLED 95 | #endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ 96 | -------------------------------------------------------------------------------- /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) { 29 | for (auto &myPair : m_uuidMap) { 30 | if (myPair.second->getUUID().equals(uuid)) { 31 | return myPair.second; 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, 56 | BLEService *service) { 57 | m_uuidMap.insert(std::pair(uuid.toString(), service)); 58 | } // setByUUID 59 | 60 | 61 | /** 62 | * @brief Set the service by handle. 63 | * @param [in] handle The handle of the service. 64 | * @param [in] service The service to cache. 65 | * @return N/A. 66 | */ 67 | void BLEServiceMap::setByHandle(uint16_t handle, 68 | BLEService* service) { 69 | m_handleMap.insert(std::pair(handle, service)); 70 | } // setByHandle 71 | 72 | 73 | /** 74 | * @brief Return a string representation of the service map. 75 | * @return A string representation of the service map. 76 | */ 77 | std::string BLEServiceMap::toString() { 78 | std::stringstream stringStream; 79 | stringStream << std::hex << std::setfill('0'); 80 | for (auto &myPair: m_handleMap) { 81 | stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; 82 | } 83 | return stringStream.str(); 84 | } // toString 85 | 86 | void BLEServiceMap::handleGATTServerEvent( 87 | esp_gatts_cb_event_t event, 88 | esp_gatt_if_t gatts_if, 89 | esp_ble_gatts_cb_param_t *param) { 90 | // Invoke the handler for every Service we have. 91 | for (auto &myPair : m_uuidMap) { 92 | myPair.second->handleGATTServerEvent(event, gatts_if, param); 93 | } 94 | } 95 | #endif /* CONFIG_BT_ENABLED */ 96 | -------------------------------------------------------------------------------- /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 16 | #include "BLEUUID.h" 17 | static const char* LOG_TAG = "BLEUUID"; 18 | 19 | #ifdef ARDUINO_ARCH_ESP32 20 | #include "esp32-hal-log.h" 21 | #endif 22 | 23 | /** 24 | * @brief Copy memory from source to target but in reverse order. 25 | * 26 | * When we move memory from one location it is normally: 27 | * 28 | * ``` 29 | * [0][1][2]...[n] -> [0][1][2]...[n] 30 | * ``` 31 | * 32 | * with this function, it is: 33 | * 34 | * ``` 35 | * [0][1][2]...[n] -> [n][n-1][n-2]...[0] 36 | * ``` 37 | * 38 | * @param [in] target The target of the copy 39 | * @param [in] source The source of the copy 40 | * @param [in] size The number of bytes to copy 41 | */ 42 | static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { 43 | assert(size > 0); 44 | target+=(size-1); // Point target to the last byte of the target data 45 | while (size > 0) { 46 | *target = *source; 47 | target--; 48 | source++; 49 | size--; 50 | } 51 | } // memrcpy 52 | 53 | 54 | /** 55 | * @brief Create a UUID from a string. 56 | * 57 | * Create a UUID from a string. There will be two possible stories here. Either the string represents 58 | * a binary data field or the string represents a hex encoding of a UUID. 59 | * For the hex encoding, here is an example: 60 | * 61 | * ``` 62 | * "beb5483e-36e1-4688-b7f5-ea07361b26a8" 63 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 64 | * 12345678-90ab-cdef-1234-567890abcdef 65 | * ``` 66 | * 67 | * This has a length of 36 characters. We need to parse this into 16 bytes. 68 | * 69 | * @param [in] value The string to build a UUID from. 70 | */ 71 | BLEUUID::BLEUUID(std::string value) { 72 | m_valueSet = true; 73 | if (value.length() == 2) { 74 | m_uuid.len = ESP_UUID_LEN_16; 75 | m_uuid.uuid.uuid16 = value[0] | (value[1] << 8); 76 | } 77 | else if (value.length() == 4) { 78 | m_uuid.len = ESP_UUID_LEN_32; 79 | m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); 80 | } 81 | else if (value.length() == 16) { 82 | m_uuid.len = ESP_UUID_LEN_128; 83 | memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); 84 | } 85 | else if (value.length() == 36) { 86 | // If the length of the string is 36 bytes then we will assume it is a long hex string in 87 | // UUID format. 88 | m_uuid.len = ESP_UUID_LEN_128; 89 | int vals[16]; 90 | sscanf(value.c_str(), "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x", 91 | &vals[15], 92 | &vals[14], 93 | &vals[13], 94 | &vals[12], 95 | &vals[11], 96 | &vals[10], 97 | &vals[9], 98 | &vals[8], 99 | &vals[7], 100 | &vals[6], 101 | &vals[5], 102 | &vals[4], 103 | &vals[3], 104 | &vals[2], 105 | &vals[1], 106 | &vals[0] 107 | ); 108 | 109 | int i; 110 | for (i=0; i<16; i++) { 111 | m_uuid.uuid.uuid128[i] = vals[i]; 112 | } 113 | } 114 | else { 115 | ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes"); 116 | m_valueSet = false; 117 | } 118 | } //BLEUUID(std::string) 119 | 120 | 121 | /** 122 | * @brief Create a UUID from 16 bytes of memory. 123 | * 124 | * @param [in] pData The pointer to the start of the UUID. 125 | * @param [in] size The size of the data. 126 | * @param [in] msbFirst Is the MSB first in pData memory? 127 | */ 128 | BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { 129 | if (size != 16) { 130 | ESP_LOGE(LOG_TAG, "ERROR: UUID length not 16 bytes"); 131 | return; 132 | } 133 | m_uuid.len = ESP_UUID_LEN_128; 134 | if (msbFirst) { 135 | memrcpy(m_uuid.uuid.uuid128, pData, 16); 136 | } else { 137 | memcpy(m_uuid.uuid.uuid128, pData, 16); 138 | } 139 | m_valueSet = true; 140 | } // BLEUUID 141 | 142 | 143 | /** 144 | * @brief Create a UUID from the 16bit value. 145 | * 146 | * @param [in] uuid The 16bit short form UUID. 147 | */ 148 | BLEUUID::BLEUUID(uint16_t uuid) { 149 | m_uuid.len = ESP_UUID_LEN_16; 150 | m_uuid.uuid.uuid16 = uuid; 151 | m_valueSet = true; 152 | 153 | } // BLEUUID 154 | 155 | 156 | /** 157 | * @brief Create a UUID from the 32bit value. 158 | * 159 | * @param [in] uuid The 32bit short form UUID. 160 | */ 161 | BLEUUID::BLEUUID(uint32_t uuid) { 162 | m_uuid.len = ESP_UUID_LEN_32; 163 | m_uuid.uuid.uuid32 = uuid; 164 | m_valueSet = true; 165 | } // BLEUUID 166 | 167 | 168 | /** 169 | * @brief Create a UUID from the native UUID. 170 | * 171 | * @param [in] uuid The native UUID. 172 | */ 173 | BLEUUID::BLEUUID(esp_bt_uuid_t uuid) { 174 | m_uuid = uuid; 175 | m_valueSet = true; 176 | } // BLEUUID 177 | 178 | 179 | /** 180 | * @brief Create a UUID from the ESP32 esp_gat_id_t. 181 | * 182 | * @param [in] gattId The data to create the UUID from. 183 | */ 184 | BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) { 185 | } // BLEUUID 186 | 187 | 188 | BLEUUID::BLEUUID() { 189 | m_valueSet = false; 190 | } // BLEUUID 191 | 192 | 193 | /** 194 | * @brief Get the number of bits in this uuid. 195 | * @return The number of bits in the UUID. One of 16, 32 or 128. 196 | */ 197 | int BLEUUID::bitSize() { 198 | if (m_valueSet == false) { 199 | return 0; 200 | } 201 | switch(m_uuid.len) { 202 | case ESP_UUID_LEN_16: { 203 | return 16; 204 | } 205 | case ESP_UUID_LEN_32: { 206 | return 32; 207 | } 208 | case ESP_UUID_LEN_128: { 209 | return 128; 210 | } 211 | default: { 212 | ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); 213 | return 0; 214 | } 215 | } // End of switch 216 | } // bitSize 217 | 218 | 219 | /** 220 | * @brief Compare a UUID against this UUID. 221 | * 222 | * @param [in] uuid The UUID to compare against. 223 | * @return True if the UUIDs are equal and false otherwise. 224 | */ 225 | bool BLEUUID::equals(BLEUUID uuid) { 226 | //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str()); 227 | if (m_valueSet == false || uuid.m_valueSet == false) { 228 | return false; 229 | } 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 == false || 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 == false) { // If we have no value, nothing to format. 360 | return ""; 361 | } 362 | 363 | // If the UUIDs are 16 or 32 bit, pad correctly. 364 | std::stringstream ss; 365 | 366 | if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. 367 | ss << "0000" << 368 | std::hex << 369 | std::setfill('0') << 370 | std::setw(4) << 371 | m_uuid.uuid.uuid16 << 372 | "-0000-1000-8000-00805f9b34fb"; 373 | return ss.str(); // Return the string 374 | } // End 16bit UUID 375 | 376 | if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. 377 | ss << std::hex << 378 | std::setfill('0') << 379 | std::setw(8) << 380 | m_uuid.uuid.uuid32 << 381 | "-0000-1000-8000-00805f9b34fb"; 382 | return ss.str(); // return the string 383 | } // End 32bit UUID 384 | 385 | // The UUID is not 16bit or 32bit which means that it is 128bit. 386 | // 387 | // UUID string format: 388 | // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP 389 | // 390 | ss << std::hex << std::setfill('0') << 391 | std::setw(2) << (int)m_uuid.uuid.uuid128[15] << 392 | std::setw(2) << (int)m_uuid.uuid.uuid128[14] << 393 | std::setw(2) << (int)m_uuid.uuid.uuid128[13] << 394 | std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << 395 | std::setw(2) << (int)m_uuid.uuid.uuid128[11] << 396 | std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << 397 | std::setw(2) << (int)m_uuid.uuid.uuid128[9] << 398 | std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << 399 | std::setw(2) << (int)m_uuid.uuid.uuid128[7] << 400 | std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << 401 | std::setw(2) << (int)m_uuid.uuid.uuid128[5] << 402 | std::setw(2) << (int)m_uuid.uuid.uuid128[4] << 403 | std::setw(2) << (int)m_uuid.uuid.uuid128[3] << 404 | std::setw(2) << (int)m_uuid.uuid.uuid128[2] << 405 | std::setw(2) << (int)m_uuid.uuid.uuid128[1] << 406 | std::setw(2) << (int)m_uuid.uuid.uuid128[0]; 407 | return ss.str(); 408 | } // toString 409 | 410 | #endif /* CONFIG_BT_ENABLED */ 411 | -------------------------------------------------------------------------------- /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 | int 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; // Is there a value set for this instance. 37 | }; // BLEUUID 38 | #endif /* CONFIG_BT_ENABLED */ 39 | #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ 40 | -------------------------------------------------------------------------------- /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 | 10 | #include 11 | 12 | #include "BLEValue.h" 13 | #ifdef ARDUINO_ARCH_ESP32 14 | #include "esp32-hal-log.h" 15 | #endif 16 | 17 | static const char* LOG_TAG="BLEValue"; 18 | 19 | BLEValue::BLEValue() { 20 | m_accumulation = ""; 21 | m_value = ""; 22 | m_readOffset = 0; 23 | } // BLEValue 24 | 25 | 26 | /** 27 | * @brief Add a message part to the accumulation. 28 | * The accumulation is a growing set of data that is added to until a commit or cancel. 29 | * @param [in] part A message part being added. 30 | */ 31 | void BLEValue::addPart(std::string part) { 32 | ESP_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); 33 | m_accumulation += part; 34 | } // addPart 35 | 36 | 37 | /** 38 | * @brief Add a message part to the accumulation. 39 | * The accumulation is a growing set of data that is added to until a commit or cancel. 40 | * @param [in] pData A message part being added. 41 | * @param [in] length The number of bytes being added. 42 | */ 43 | void BLEValue::addPart(uint8_t* pData, size_t length) { 44 | ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length); 45 | m_accumulation += std::string((char *)pData, length); 46 | } // addPart 47 | 48 | 49 | /** 50 | * @brief Cancel the current accumulation. 51 | */ 52 | void BLEValue::cancel() { 53 | ESP_LOGD(LOG_TAG, ">> cancel"); 54 | m_accumulation = ""; 55 | m_readOffset = 0; 56 | } // cancel 57 | 58 | 59 | /** 60 | * @brief Commit the current accumulation. 61 | * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces 62 | * of the overall message. After the last part has been received, we may perform a commit which means that 63 | * we now have the complete message and commit the change as a unit. 64 | */ 65 | void BLEValue::commit() { 66 | ESP_LOGD(LOG_TAG, ">> commit"); 67 | // If there is nothing to commit, do nothing. 68 | if (m_accumulation.length() == 0) { 69 | return; 70 | } 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 | 140 | 141 | 142 | #endif // CONFIG_BT_ENABLED 143 | -------------------------------------------------------------------------------- /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 | #endif // CONFIG_BT_ENABLED 38 | #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ 39 | -------------------------------------------------------------------------------- /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 15 | #include "sdkconfig.h" 16 | 17 | static const char* LOG_TAG = "FreeRTOS"; 18 | 19 | /** 20 | * Sleep for the specified number of milliseconds. 21 | * @param[in] ms The period in milliseconds for which to sleep. 22 | */ 23 | void FreeRTOS::sleep(uint32_t ms) { 24 | ::vTaskDelay(ms/portTICK_PERIOD_MS); 25 | } // sleep 26 | 27 | 28 | /** 29 | * Start a new task. 30 | * @param[in] task The function pointer to the function to be run in the task. 31 | * @param[in] taskName A string identifier for the task. 32 | * @param[in] param An optional parameter to be passed to the started task. 33 | * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. 34 | */ 35 | void FreeRTOS::startTask(void task(void*), std::string taskName, void *param, int stackSize) { 36 | ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); 37 | } // startTask 38 | 39 | 40 | /** 41 | * Delete the task. 42 | * @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted. 43 | */ 44 | void FreeRTOS::deleteTask(TaskHandle_t pTask) { 45 | ::vTaskDelete(pTask); 46 | } // deleteTask 47 | 48 | 49 | /** 50 | * Get the time in milliseconds since the %FreeRTOS scheduler started. 51 | * @return The time in milliseconds since the %FreeRTOS scheduler started. 52 | */ 53 | uint32_t FreeRTOS::getTimeSinceStart() { 54 | return (uint32_t)(xTaskGetTickCount()*portTICK_PERIOD_MS); 55 | } // getTimeSinceStart 56 | 57 | /* 58 | * public: 59 | Semaphore(std::string = ""); 60 | ~Semaphore(); 61 | void give(); 62 | void take(std::string owner=""); 63 | void take(uint32_t timeoutMs, std::string owner=""); 64 | private: 65 | SemaphoreHandle_t m_semaphore; 66 | std::string m_name; 67 | std::string m_owner; 68 | }; 69 | * 70 | */ 71 | 72 | /** 73 | * @brief Wait for a semaphore to be released by trying to take it and 74 | * then releasing it again. 75 | * @param [in] owner A debug tag. 76 | * @return The value associated with the semaphore. 77 | */ 78 | uint32_t FreeRTOS::Semaphore::wait(std::string owner) { 79 | ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); 80 | 81 | 82 | if (m_usePthreads) { 83 | pthread_mutex_lock(&m_pthread_mutex); 84 | } else { 85 | xSemaphoreTake(m_semaphore, portMAX_DELAY); 86 | } 87 | 88 | m_owner = owner; 89 | 90 | if (m_usePthreads) { 91 | pthread_mutex_unlock(&m_pthread_mutex); 92 | } else { 93 | xSemaphoreGive(m_semaphore); 94 | } 95 | 96 | ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); 97 | m_owner = std::string(""); 98 | return m_value; 99 | } // wait 100 | 101 | 102 | FreeRTOS::Semaphore::Semaphore(std::string name) { 103 | m_usePthreads = false; // Are we using pThreads or FreeRTOS? 104 | if (m_usePthreads) { 105 | pthread_mutex_init(&m_pthread_mutex, nullptr); 106 | } else { 107 | m_semaphore = xSemaphoreCreateMutex(); 108 | } 109 | 110 | m_name = name; 111 | m_owner = std::string(""); 112 | m_value = 0; 113 | } 114 | 115 | 116 | FreeRTOS::Semaphore::~Semaphore() { 117 | if (m_usePthreads) { 118 | pthread_mutex_destroy(&m_pthread_mutex); 119 | } else { 120 | vSemaphoreDelete(m_semaphore); 121 | } 122 | } 123 | 124 | 125 | /** 126 | * @brief Give a semaphore. 127 | * The Semaphore is given. 128 | */ 129 | void FreeRTOS::Semaphore::give() { 130 | ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); 131 | if (m_usePthreads) { 132 | pthread_mutex_unlock(&m_pthread_mutex); 133 | } else { 134 | xSemaphoreGive(m_semaphore); 135 | } 136 | // #ifdef ARDUINO_ARCH_ESP32 137 | // FreeRTOS::sleep(10); 138 | // #endif 139 | 140 | m_owner = std::string(""); 141 | } // Semaphore::give 142 | 143 | 144 | /** 145 | * @brief Give a semaphore. 146 | * The Semaphore is given with an associated value. 147 | * @param [in] value The value to associate with the semaphore. 148 | */ 149 | void FreeRTOS::Semaphore::give(uint32_t value) { 150 | m_value = value; 151 | give(); 152 | } // give 153 | 154 | 155 | /** 156 | * @brief Give a semaphore from an ISR. 157 | */ 158 | void FreeRTOS::Semaphore::giveFromISR() { 159 | BaseType_t higherPriorityTaskWoken; 160 | if (m_usePthreads) { 161 | assert(false); 162 | } else { 163 | xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); 164 | } 165 | } // giveFromISR 166 | 167 | 168 | /** 169 | * @brief Take a semaphore. 170 | * Take a semaphore and wait indefinitely. 171 | * @param [in] owner The new owner (for debugging) 172 | * @return True if we took the semaphore. 173 | */ 174 | bool FreeRTOS::Semaphore::take(std::string owner) 175 | { 176 | ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); 177 | bool rc = false; 178 | if (m_usePthreads) { 179 | pthread_mutex_lock(&m_pthread_mutex); 180 | } else { 181 | rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY); 182 | } 183 | m_owner = owner; 184 | if (rc) { 185 | ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); 186 | } else { 187 | ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); 188 | } 189 | return rc; 190 | } // Semaphore::take 191 | 192 | 193 | /** 194 | * @brief Take a semaphore. 195 | * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. 196 | * @param [in] timeoutMs Timeout in milliseconds. 197 | * @param [in] owner The new owner (for debugging) 198 | * @return True if we took the semaphore. 199 | */ 200 | bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { 201 | 202 | ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); 203 | bool rc = false; 204 | if (m_usePthreads) { 205 | assert(false); // We apparently don't have a timed wait for pthreads. 206 | } else { 207 | rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); 208 | } 209 | m_owner = owner; 210 | if (rc) { 211 | ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); 212 | } else { 213 | ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); 214 | } 215 | return rc; 216 | } // Semaphore::take 217 | 218 | 219 | 220 | /** 221 | * @brief Create a string representation of the semaphore. 222 | * @return A string representation of the semaphore. 223 | */ 224 | std::string FreeRTOS::Semaphore::toString() { 225 | std::stringstream stringStream; 226 | stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; 227 | return stringStream.str(); 228 | } // toString 229 | 230 | 231 | /** 232 | * @brief Set the name of the semaphore. 233 | * @param [in] name The name of the semaphore. 234 | */ 235 | void FreeRTOS::Semaphore::setName(std::string name) { 236 | m_name = name; 237 | } // setName 238 | 239 | 240 | /** 241 | * @brief Create a ring buffer. 242 | * @param [in] length The amount of storage to allocate for the ring buffer. 243 | * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF. 244 | */ 245 | Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) { 246 | m_handle = ::xRingbufferCreate(length, type); 247 | } // Ringbuffer 248 | 249 | 250 | Ringbuffer::~Ringbuffer() { 251 | ::vRingbufferDelete(m_handle); 252 | } // ~Ringbuffer 253 | 254 | 255 | /** 256 | * @brief Receive data from the buffer. 257 | * @param [out] size On return, the size of data returned. 258 | * @param [in] wait How long to wait. 259 | * @return A pointer to the storage retrieved. 260 | */ 261 | void* Ringbuffer::receive(size_t* size, TickType_t wait) { 262 | return ::xRingbufferReceive(m_handle, size, wait); 263 | } // receive 264 | 265 | 266 | /** 267 | * @brief Return an item. 268 | * @param [in] item The item to be returned/released. 269 | */ 270 | void Ringbuffer::returnItem(void* item) { 271 | ::vRingbufferReturnItem(m_handle, item); 272 | } // returnItem 273 | 274 | 275 | /** 276 | * @brief Send data to the buffer. 277 | * @param [in] data The data to place into the buffer. 278 | * @param [in] length The length of data to place into the buffer. 279 | * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. 280 | * @return 281 | */ 282 | uint32_t Ringbuffer::send(void* data, size_t length, TickType_t wait) { 283 | return ::xRingbufferSend(m_handle, data, length, wait); 284 | } // send 285 | 286 | 287 | -------------------------------------------------------------------------------- /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, int 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 | * @brief Ringbuffer. 57 | */ 58 | class Ringbuffer { 59 | public: 60 | Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT); 61 | ~Ringbuffer(); 62 | 63 | void* receive(size_t* size, TickType_t wait = portMAX_DELAY); 64 | void returnItem(void* item); 65 | uint32_t send(void* data, size_t length, TickType_t wait = portMAX_DELAY); 66 | private: 67 | RingbufHandle_t m_handle; 68 | }; 69 | 70 | #endif /* MAIN_FREERTOS_H_ */ 71 | -------------------------------------------------------------------------------- /src/GeneralUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * GeneralUtils.cpp 3 | * 4 | * Created on: May 20, 2017 5 | * Author: kolban 6 | */ 7 | 8 | #include "GeneralUtils.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | static const char* LOG_TAG = "GeneralUtils"; 24 | 25 | static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 26 | "abcdefghijklmnopqrstuvwxyz" 27 | "0123456789+/"; 28 | 29 | static int base64EncodedLength(size_t length) { 30 | return (length + 2 - ((length + 2) % 3)) / 3 * 4; 31 | } // base64EncodedLength 32 | 33 | 34 | static int base64EncodedLength(const std::string &in) { 35 | return base64EncodedLength(in.length()); 36 | } // base64EncodedLength 37 | 38 | 39 | static void a3_to_a4(unsigned char * a4, unsigned char * a3) { 40 | a4[0] = (a3[0] & 0xfc) >> 2; 41 | a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); 42 | a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); 43 | a4[3] = (a3[2] & 0x3f); 44 | } // a3_to_a4 45 | 46 | 47 | static void a4_to_a3(unsigned char * a3, unsigned char * a4) { 48 | a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); 49 | a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); 50 | a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; 51 | } // a4_to_a3 52 | 53 | 54 | /** 55 | * @brief Encode a string into base 64. 56 | * @param [in] in 57 | * @param [out] out 58 | */ 59 | bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { 60 | int i = 0, j = 0; 61 | size_t enc_len = 0; 62 | unsigned char a3[3]; 63 | unsigned char a4[4]; 64 | 65 | out->resize(base64EncodedLength(in)); 66 | 67 | int input_len = in.size(); 68 | std::string::const_iterator input = in.begin(); 69 | 70 | while (input_len--) { 71 | a3[i++] = *(input++); 72 | if (i == 3) { 73 | a3_to_a4(a4, a3); 74 | 75 | for (i = 0; i < 4; i++) { 76 | (*out)[enc_len++] = kBase64Alphabet[a4[i]]; 77 | } 78 | 79 | i = 0; 80 | } 81 | } 82 | 83 | if (i) { 84 | for (j = i; j < 3; j++) { 85 | a3[j] = '\0'; 86 | } 87 | 88 | a3_to_a4(a4, a3); 89 | 90 | for (j = 0; j < i + 1; j++) { 91 | (*out)[enc_len++] = kBase64Alphabet[a4[j]]; 92 | } 93 | 94 | while ((i++ < 3)) { 95 | (*out)[enc_len++] = '='; 96 | } 97 | } 98 | 99 | return (enc_len == out->size()); 100 | } // base64Encode 101 | 102 | 103 | /** 104 | * @brief Dump general info to the log. 105 | * Data includes: 106 | * * Amount of free RAM 107 | */ 108 | void GeneralUtils::dumpInfo() { 109 | size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); 110 | esp_chip_info_t chipInfo; 111 | esp_chip_info(&chipInfo); 112 | ESP_LOGD(LOG_TAG, "--- dumpInfo ---"); 113 | ESP_LOGD(LOG_TAG, "Free heap: %d", freeHeap); 114 | ESP_LOGD(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); 115 | ESP_LOGD(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); 116 | ESP_LOGD(LOG_TAG, "---"); 117 | } // dumpInfo 118 | 119 | 120 | /** 121 | * @brief Does the string end with a specific character? 122 | * @param [in] str The string to examine. 123 | * @param [in] c The character to look form. 124 | * @return True if the string ends with the given character. 125 | */ 126 | bool GeneralUtils::endsWith(std::string str, char c) { 127 | if (str.empty()) { 128 | return false; 129 | } 130 | if (str.at(str.length()-1) == c) { 131 | return true; 132 | } 133 | return false; 134 | } // endsWidth 135 | 136 | 137 | static int DecodedLength(const std::string &in) { 138 | int numEq = 0; 139 | int n = in.size(); 140 | 141 | for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { 142 | ++numEq; 143 | } 144 | return ((6 * n) / 8) - numEq; 145 | } // DecodedLength 146 | 147 | 148 | static unsigned char b64_lookup(unsigned char c) { 149 | if(c >='A' && c <='Z') return c - 'A'; 150 | if(c >='a' && c <='z') return c - 71; 151 | if(c >='0' && c <='9') return c + 4; 152 | if(c == '+') return 62; 153 | if(c == '/') return 63; 154 | return 255; 155 | }; // b64_lookup 156 | 157 | 158 | /** 159 | * @brief Decode a chunk of data that is base64 encoded. 160 | * @param [in] in The string to be decoded. 161 | * @param [out] out The resulting data. 162 | */ 163 | bool GeneralUtils::base64Decode(const std::string &in, std::string *out) { 164 | int i = 0, j = 0; 165 | size_t dec_len = 0; 166 | unsigned char a3[3]; 167 | unsigned char a4[4]; 168 | 169 | int input_len = in.size(); 170 | std::string::const_iterator input = in.begin(); 171 | 172 | out->resize(DecodedLength(in)); 173 | 174 | while (input_len--) { 175 | if (*input == '=') { 176 | break; 177 | } 178 | 179 | a4[i++] = *(input++); 180 | if (i == 4) { 181 | for (i = 0; i <4; i++) { 182 | a4[i] = b64_lookup(a4[i]); 183 | } 184 | 185 | a4_to_a3(a3,a4); 186 | 187 | for (i = 0; i < 3; i++) { 188 | (*out)[dec_len++] = a3[i]; 189 | } 190 | 191 | i = 0; 192 | } 193 | } 194 | 195 | if (i) { 196 | for (j = i; j < 4; j++) { 197 | a4[j] = '\0'; 198 | } 199 | 200 | for (j = 0; j < 4; j++) { 201 | a4[j] = b64_lookup(a4[j]); 202 | } 203 | 204 | a4_to_a3(a3,a4); 205 | 206 | for (j = 0; j < i - 1; j++) { 207 | (*out)[dec_len++] = a3[j]; 208 | } 209 | } 210 | 211 | return (dec_len == out->size()); 212 | } // base64Decode 213 | 214 | /* 215 | void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { 216 | uint32_t index=0; 217 | std::stringstream ascii; 218 | std::stringstream hex; 219 | char asciiBuf[80]; 220 | char hexBuf[80]; 221 | hex.str(""); 222 | ascii.str(""); 223 | while(index < length) { 224 | hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' '; 225 | if (std::isprint(pData[index])) { 226 | ascii << pData[index]; 227 | } else { 228 | ascii << '.'; 229 | } 230 | index++; 231 | if (index % 16 == 0) { 232 | strcpy(hexBuf, hex.str().c_str()); 233 | strcpy(asciiBuf, ascii.str().c_str()); 234 | ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); 235 | hex.str(""); 236 | ascii.str(""); 237 | } 238 | } 239 | if (index %16 != 0) { 240 | while(index % 16 != 0) { 241 | hex << " "; 242 | index++; 243 | } 244 | strcpy(hexBuf, hex.str().c_str()); 245 | strcpy(asciiBuf, ascii.str().c_str()); 246 | ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); 247 | //ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); 248 | } 249 | FreeRTOS::sleep(1000); 250 | } 251 | */ 252 | 253 | /* 254 | void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { 255 | uint32_t index=0; 256 | static std::stringstream ascii; 257 | static std::stringstream hex; 258 | hex.str(""); 259 | ascii.str(""); 260 | while(index < length) { 261 | hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' '; 262 | if (std::isprint(pData[index])) { 263 | ascii << pData[index]; 264 | } else { 265 | ascii << '.'; 266 | } 267 | index++; 268 | if (index % 16 == 0) { 269 | ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); 270 | hex.str(""); 271 | ascii.str(""); 272 | } 273 | } 274 | if (index %16 != 0) { 275 | while(index % 16 != 0) { 276 | hex << " "; 277 | index++; 278 | } 279 | ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); 280 | } 281 | FreeRTOS::sleep(1000); 282 | } 283 | */ 284 | 285 | 286 | /** 287 | * @brief Dump a representation of binary data to the console. 288 | * 289 | * @param [in] pData Pointer to the start of data to be logged. 290 | * @param [in] length Length of the data (in bytes) to be logged. 291 | * @return N/A. 292 | */ 293 | void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { 294 | char ascii[80]; 295 | char hex[80]; 296 | char tempBuf[80]; 297 | uint32_t lineNumber = 0; 298 | 299 | ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ----------------"); 300 | strcpy(ascii, ""); 301 | strcpy(hex, ""); 302 | uint32_t index=0; 303 | while(index < length) { 304 | sprintf(tempBuf, "%.2x ", pData[index]); 305 | strcat(hex, tempBuf); 306 | if (isprint(pData[index])) { 307 | sprintf(tempBuf, "%c", pData[index]); 308 | } else { 309 | sprintf(tempBuf, "."); 310 | } 311 | strcat(ascii, tempBuf); 312 | index++; 313 | if (index % 16 == 0) { 314 | ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); 315 | strcpy(ascii, ""); 316 | strcpy(hex, ""); 317 | lineNumber++; 318 | } 319 | } 320 | if (index %16 != 0) { 321 | while(index % 16 != 0) { 322 | strcat(hex, " "); 323 | index++; 324 | } 325 | ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); 326 | } 327 | } // hexDump 328 | 329 | 330 | /** 331 | * @brief Convert an IP address to string. 332 | * @param ip The 4 byte IP address. 333 | * @return A string representation of the IP address. 334 | */ 335 | std::string GeneralUtils::ipToString(uint8_t *ip) { 336 | std::stringstream s; 337 | s << (int)ip[0] << '.' << (int)ip[1] << '.' << (int)ip[2] << '.' << (int)ip[3]; 338 | return s.str(); 339 | } // ipToString 340 | 341 | 342 | /** 343 | * @brief Split a string into parts based on a delimiter. 344 | * @param [in] source The source string to split. 345 | * @param [in] delimiter The delimiter characters. 346 | * @return A vector of strings that are the split of the input. 347 | */ 348 | std::vector GeneralUtils::split(std::string source, char delimiter) { 349 | // See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g 350 | std::vector strings; 351 | std::istringstream iss(source); 352 | std::string s; 353 | while(std::getline(iss, s, delimiter)) { 354 | strings.push_back(trim(s)); 355 | } 356 | return strings; 357 | } // split 358 | 359 | 360 | /** 361 | * @brief Convert an ESP error code to a string. 362 | * @param [in] errCode The errCode to be converted. 363 | * @return A string representation of the error code. 364 | */ 365 | const char* GeneralUtils::errorToString(esp_err_t errCode) { 366 | switch(errCode) { 367 | case ESP_OK: 368 | return "OK"; 369 | case ESP_FAIL: 370 | return "Fail"; 371 | case ESP_ERR_NO_MEM: 372 | return "No memory"; 373 | case ESP_ERR_INVALID_ARG: 374 | return "Invalid argument"; 375 | case ESP_ERR_INVALID_SIZE: 376 | return "Invalid state"; 377 | case ESP_ERR_INVALID_STATE: 378 | return "Invalid state"; 379 | case ESP_ERR_NOT_FOUND: 380 | return "Not found"; 381 | case ESP_ERR_NOT_SUPPORTED: 382 | return "Not supported"; 383 | case ESP_ERR_TIMEOUT: 384 | return "Timeout"; 385 | case ESP_ERR_NVS_NOT_INITIALIZED: 386 | return "ESP_ERR_NVS_NOT_INITIALIZED"; 387 | case ESP_ERR_NVS_NOT_FOUND: 388 | return "ESP_ERR_NVS_NOT_FOUND"; 389 | case ESP_ERR_NVS_TYPE_MISMATCH: 390 | return "ESP_ERR_NVS_TYPE_MISMATCH"; 391 | case ESP_ERR_NVS_READ_ONLY: 392 | return "ESP_ERR_NVS_READ_ONLY"; 393 | case ESP_ERR_NVS_NOT_ENOUGH_SPACE: 394 | return "ESP_ERR_NVS_NOT_ENOUGH_SPACE"; 395 | case ESP_ERR_NVS_INVALID_NAME: 396 | return "ESP_ERR_NVS_INVALID_NAME"; 397 | case ESP_ERR_NVS_INVALID_HANDLE: 398 | return "ESP_ERR_NVS_INVALID_HANDLE"; 399 | case ESP_ERR_NVS_REMOVE_FAILED: 400 | return "ESP_ERR_NVS_REMOVE_FAILED"; 401 | case ESP_ERR_NVS_KEY_TOO_LONG: 402 | return "ESP_ERR_NVS_KEY_TOO_LONG"; 403 | case ESP_ERR_NVS_PAGE_FULL: 404 | return "ESP_ERR_NVS_PAGE_FULL"; 405 | case ESP_ERR_NVS_INVALID_STATE: 406 | return "ESP_ERR_NVS_INVALID_STATE"; 407 | case ESP_ERR_NVS_INVALID_LENGTH: 408 | return "ESP_ERR_NVS_INVALID_LENGTH"; 409 | case ESP_ERR_WIFI_NOT_INIT: 410 | return "ESP_ERR_WIFI_NOT_INIT"; 411 | //case ESP_ERR_WIFI_NOT_START: 412 | // return "ESP_ERR_WIFI_NOT_START"; 413 | case ESP_ERR_WIFI_IF: 414 | return "ESP_ERR_WIFI_IF"; 415 | case ESP_ERR_WIFI_MODE: 416 | return "ESP_ERR_WIFI_MODE"; 417 | case ESP_ERR_WIFI_STATE: 418 | return "ESP_ERR_WIFI_STATE"; 419 | case ESP_ERR_WIFI_CONN: 420 | return "ESP_ERR_WIFI_CONN"; 421 | case ESP_ERR_WIFI_NVS: 422 | return "ESP_ERR_WIFI_NVS"; 423 | case ESP_ERR_WIFI_MAC: 424 | return "ESP_ERR_WIFI_MAC"; 425 | case ESP_ERR_WIFI_SSID: 426 | return "ESP_ERR_WIFI_SSID"; 427 | case ESP_ERR_WIFI_PASSWORD: 428 | return "ESP_ERR_WIFI_PASSWORD"; 429 | case ESP_ERR_WIFI_TIMEOUT: 430 | return "ESP_ERR_WIFI_TIMEOUT"; 431 | case ESP_ERR_WIFI_WAKE_FAIL: 432 | return "ESP_ERR_WIFI_WAKE_FAIL"; 433 | } 434 | return "Unknown ESP_ERR error"; 435 | } // errorToString 436 | 437 | 438 | /** 439 | * @brief Convert a string to lower case. 440 | * @param [in] value The string to convert to lower case. 441 | * @return A lower case representation of the string. 442 | */ 443 | std::string GeneralUtils::toLower(std::string& value) { 444 | // Question: Could this be improved with a signature of: 445 | // std::string& GeneralUtils::toLower(std::string& value) 446 | std::transform(value.begin(), value.end(), value.begin(), ::tolower); 447 | return value; 448 | } // toLower 449 | 450 | 451 | /** 452 | * @brief Remove white space from a string. 453 | */ 454 | std::string GeneralUtils::trim(const std::string& str) 455 | { 456 | size_t first = str.find_first_not_of(' '); 457 | if (std::string::npos == first) 458 | { 459 | return str; 460 | } 461 | size_t last = str.find_last_not_of(' '); 462 | return str.substr(first, (last - first + 1)); 463 | } // trim 464 | 465 | 466 | -------------------------------------------------------------------------------- /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 void hexDump(const uint8_t* pData, uint32_t length); 27 | static std::string ipToString(uint8_t* ip); 28 | static std::vector split(std::string source, char delimiter); 29 | static std::string toLower(std::string& value); 30 | static std::string trim(const std::string& str); 31 | 32 | }; 33 | 34 | #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ 35 | -------------------------------------------------------------------------------- /src/HIDKeyboardTypes.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015 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 | * Note: this file was pulled from different parts of the USBHID library, in mbed SDK 19 | */ 20 | 21 | #ifndef KEYBOARD_DEFS_H 22 | #define KEYBOARD_DEFS_H 23 | 24 | #define REPORT_ID_KEYBOARD 1 25 | #define REPORT_ID_VOLUME 3 26 | 27 | /* Modifiers */ 28 | enum MODIFIER_KEY { 29 | KEY_CTRL = 1, 30 | KEY_SHIFT = 2, 31 | KEY_ALT = 4, 32 | }; 33 | 34 | 35 | enum MEDIA_KEY { 36 | KEY_NEXT_TRACK, /*!< next Track Button */ 37 | KEY_PREVIOUS_TRACK, /*!< Previous track Button */ 38 | KEY_STOP, /*!< Stop Button */ 39 | KEY_PLAY_PAUSE, /*!< Play/Pause Button */ 40 | KEY_MUTE, /*!< Mute Button */ 41 | KEY_VOLUME_UP, /*!< Volume Up Button */ 42 | KEY_VOLUME_DOWN, /*!< Volume Down Button */ 43 | }; 44 | 45 | enum FUNCTION_KEY { 46 | KEY_F1 = 128, /* F1 key */ 47 | KEY_F2, /* F2 key */ 48 | KEY_F3, /* F3 key */ 49 | KEY_F4, /* F4 key */ 50 | KEY_F5, /* F5 key */ 51 | KEY_F6, /* F6 key */ 52 | KEY_F7, /* F7 key */ 53 | KEY_F8, /* F8 key */ 54 | KEY_F9, /* F9 key */ 55 | KEY_F10, /* F10 key */ 56 | KEY_F11, /* F11 key */ 57 | KEY_F12, /* F12 key */ 58 | 59 | KEY_PRINT_SCREEN, /* Print Screen key */ 60 | KEY_SCROLL_LOCK, /* Scroll lock */ 61 | KEY_CAPS_LOCK, /* caps lock */ 62 | KEY_NUM_LOCK, /* num lock */ 63 | KEY_INSERT, /* Insert key */ 64 | KEY_HOME, /* Home key */ 65 | KEY_PAGE_UP, /* Page Up key */ 66 | KEY_PAGE_DOWN, /* Page Down key */ 67 | 68 | RIGHT_ARROW, /* Right arrow */ 69 | LEFT_ARROW, /* Left arrow */ 70 | DOWN_ARROW, /* Down arrow */ 71 | UP_ARROW, /* Up arrow */ 72 | }; 73 | 74 | typedef struct { 75 | unsigned char usage; 76 | unsigned char modifier; 77 | } KEYMAP; 78 | 79 | #ifdef US_KEYBOARD 80 | /* US keyboard (as HID standard) */ 81 | #define KEYMAP_SIZE (152) 82 | const KEYMAP keymap[KEYMAP_SIZE] = { 83 | {0, 0}, /* NUL */ 84 | {0, 0}, /* SOH */ 85 | {0, 0}, /* STX */ 86 | {0, 0}, /* ETX */ 87 | {0, 0}, /* EOT */ 88 | {0, 0}, /* ENQ */ 89 | {0, 0}, /* ACK */ 90 | {0, 0}, /* BEL */ 91 | {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ 92 | {0x2b, 0}, /* TAB */ /* Keyboard Tab */ 93 | {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ 94 | {0, 0}, /* VT */ 95 | {0, 0}, /* FF */ 96 | {0, 0}, /* CR */ 97 | {0, 0}, /* SO */ 98 | {0, 0}, /* SI */ 99 | {0, 0}, /* DEL */ 100 | {0, 0}, /* DC1 */ 101 | {0, 0}, /* DC2 */ 102 | {0, 0}, /* DC3 */ 103 | {0, 0}, /* DC4 */ 104 | {0, 0}, /* NAK */ 105 | {0, 0}, /* SYN */ 106 | {0, 0}, /* ETB */ 107 | {0, 0}, /* CAN */ 108 | {0, 0}, /* EM */ 109 | {0, 0}, /* SUB */ 110 | {0, 0}, /* ESC */ 111 | {0, 0}, /* FS */ 112 | {0, 0}, /* GS */ 113 | {0, 0}, /* RS */ 114 | {0, 0}, /* US */ 115 | {0x2c, 0}, /* */ 116 | {0x1e, KEY_SHIFT}, /* ! */ 117 | {0x34, KEY_SHIFT}, /* " */ 118 | {0x20, KEY_SHIFT}, /* # */ 119 | {0x21, KEY_SHIFT}, /* $ */ 120 | {0x22, KEY_SHIFT}, /* % */ 121 | {0x24, KEY_SHIFT}, /* & */ 122 | {0x34, 0}, /* ' */ 123 | {0x26, KEY_SHIFT}, /* ( */ 124 | {0x27, KEY_SHIFT}, /* ) */ 125 | {0x25, KEY_SHIFT}, /* * */ 126 | {0x2e, KEY_SHIFT}, /* + */ 127 | {0x36, 0}, /* , */ 128 | {0x2d, 0}, /* - */ 129 | {0x37, 0}, /* . */ 130 | {0x38, 0}, /* / */ 131 | {0x27, 0}, /* 0 */ 132 | {0x1e, 0}, /* 1 */ 133 | {0x1f, 0}, /* 2 */ 134 | {0x20, 0}, /* 3 */ 135 | {0x21, 0}, /* 4 */ 136 | {0x22, 0}, /* 5 */ 137 | {0x23, 0}, /* 6 */ 138 | {0x24, 0}, /* 7 */ 139 | {0x25, 0}, /* 8 */ 140 | {0x26, 0}, /* 9 */ 141 | {0x33, KEY_SHIFT}, /* : */ 142 | {0x33, 0}, /* ; */ 143 | {0x36, KEY_SHIFT}, /* < */ 144 | {0x2e, 0}, /* = */ 145 | {0x37, KEY_SHIFT}, /* > */ 146 | {0x38, KEY_SHIFT}, /* ? */ 147 | {0x1f, KEY_SHIFT}, /* @ */ 148 | {0x04, KEY_SHIFT}, /* A */ 149 | {0x05, KEY_SHIFT}, /* B */ 150 | {0x06, KEY_SHIFT}, /* C */ 151 | {0x07, KEY_SHIFT}, /* D */ 152 | {0x08, KEY_SHIFT}, /* E */ 153 | {0x09, KEY_SHIFT}, /* F */ 154 | {0x0a, KEY_SHIFT}, /* G */ 155 | {0x0b, KEY_SHIFT}, /* H */ 156 | {0x0c, KEY_SHIFT}, /* I */ 157 | {0x0d, KEY_SHIFT}, /* J */ 158 | {0x0e, KEY_SHIFT}, /* K */ 159 | {0x0f, KEY_SHIFT}, /* L */ 160 | {0x10, KEY_SHIFT}, /* M */ 161 | {0x11, KEY_SHIFT}, /* N */ 162 | {0x12, KEY_SHIFT}, /* O */ 163 | {0x13, KEY_SHIFT}, /* P */ 164 | {0x14, KEY_SHIFT}, /* Q */ 165 | {0x15, KEY_SHIFT}, /* R */ 166 | {0x16, KEY_SHIFT}, /* S */ 167 | {0x17, KEY_SHIFT}, /* T */ 168 | {0x18, KEY_SHIFT}, /* U */ 169 | {0x19, KEY_SHIFT}, /* V */ 170 | {0x1a, KEY_SHIFT}, /* W */ 171 | {0x1b, KEY_SHIFT}, /* X */ 172 | {0x1c, KEY_SHIFT}, /* Y */ 173 | {0x1d, KEY_SHIFT}, /* Z */ 174 | {0x2f, 0}, /* [ */ 175 | {0x31, 0}, /* \ */ 176 | {0x30, 0}, /* ] */ 177 | {0x23, KEY_SHIFT}, /* ^ */ 178 | {0x2d, KEY_SHIFT}, /* _ */ 179 | {0x35, 0}, /* ` */ 180 | {0x04, 0}, /* a */ 181 | {0x05, 0}, /* b */ 182 | {0x06, 0}, /* c */ 183 | {0x07, 0}, /* d */ 184 | {0x08, 0}, /* e */ 185 | {0x09, 0}, /* f */ 186 | {0x0a, 0}, /* g */ 187 | {0x0b, 0}, /* h */ 188 | {0x0c, 0}, /* i */ 189 | {0x0d, 0}, /* j */ 190 | {0x0e, 0}, /* k */ 191 | {0x0f, 0}, /* l */ 192 | {0x10, 0}, /* m */ 193 | {0x11, 0}, /* n */ 194 | {0x12, 0}, /* o */ 195 | {0x13, 0}, /* p */ 196 | {0x14, 0}, /* q */ 197 | {0x15, 0}, /* r */ 198 | {0x16, 0}, /* s */ 199 | {0x17, 0}, /* t */ 200 | {0x18, 0}, /* u */ 201 | {0x19, 0}, /* v */ 202 | {0x1a, 0}, /* w */ 203 | {0x1b, 0}, /* x */ 204 | {0x1c, 0}, /* y */ 205 | {0x1d, 0}, /* z */ 206 | {0x2f, KEY_SHIFT}, /* { */ 207 | {0x31, KEY_SHIFT}, /* | */ 208 | {0x30, KEY_SHIFT}, /* } */ 209 | {0x35, KEY_SHIFT}, /* ~ */ 210 | {0,0}, /* DEL */ 211 | 212 | {0x3a, 0}, /* F1 */ 213 | {0x3b, 0}, /* F2 */ 214 | {0x3c, 0}, /* F3 */ 215 | {0x3d, 0}, /* F4 */ 216 | {0x3e, 0}, /* F5 */ 217 | {0x3f, 0}, /* F6 */ 218 | {0x40, 0}, /* F7 */ 219 | {0x41, 0}, /* F8 */ 220 | {0x42, 0}, /* F9 */ 221 | {0x43, 0}, /* F10 */ 222 | {0x44, 0}, /* F11 */ 223 | {0x45, 0}, /* F12 */ 224 | 225 | {0x46, 0}, /* PRINT_SCREEN */ 226 | {0x47, 0}, /* SCROLL_LOCK */ 227 | {0x39, 0}, /* CAPS_LOCK */ 228 | {0x53, 0}, /* NUM_LOCK */ 229 | {0x49, 0}, /* INSERT */ 230 | {0x4a, 0}, /* HOME */ 231 | {0x4b, 0}, /* PAGE_UP */ 232 | {0x4e, 0}, /* PAGE_DOWN */ 233 | 234 | {0x4f, 0}, /* RIGHT_ARROW */ 235 | {0x50, 0}, /* LEFT_ARROW */ 236 | {0x51, 0}, /* DOWN_ARROW */ 237 | {0x52, 0}, /* UP_ARROW */ 238 | }; 239 | 240 | #else 241 | /* UK keyboard */ 242 | #define KEYMAP_SIZE (152) 243 | const KEYMAP keymap[KEYMAP_SIZE] = { 244 | {0, 0}, /* NUL */ 245 | {0, 0}, /* SOH */ 246 | {0, 0}, /* STX */ 247 | {0, 0}, /* ETX */ 248 | {0, 0}, /* EOT */ 249 | {0, 0}, /* ENQ */ 250 | {0, 0}, /* ACK */ 251 | {0, 0}, /* BEL */ 252 | {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ 253 | {0x2b, 0}, /* TAB */ /* Keyboard Tab */ 254 | {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ 255 | {0, 0}, /* VT */ 256 | {0, 0}, /* FF */ 257 | {0, 0}, /* CR */ 258 | {0, 0}, /* SO */ 259 | {0, 0}, /* SI */ 260 | {0, 0}, /* DEL */ 261 | {0, 0}, /* DC1 */ 262 | {0, 0}, /* DC2 */ 263 | {0, 0}, /* DC3 */ 264 | {0, 0}, /* DC4 */ 265 | {0, 0}, /* NAK */ 266 | {0, 0}, /* SYN */ 267 | {0, 0}, /* ETB */ 268 | {0, 0}, /* CAN */ 269 | {0, 0}, /* EM */ 270 | {0, 0}, /* SUB */ 271 | {0, 0}, /* ESC */ 272 | {0, 0}, /* FS */ 273 | {0, 0}, /* GS */ 274 | {0, 0}, /* RS */ 275 | {0, 0}, /* US */ 276 | {0x2c, 0}, /* */ 277 | {0x1e, KEY_SHIFT}, /* ! */ 278 | {0x1f, KEY_SHIFT}, /* " */ 279 | {0x32, 0}, /* # */ 280 | {0x21, KEY_SHIFT}, /* $ */ 281 | {0x22, KEY_SHIFT}, /* % */ 282 | {0x24, KEY_SHIFT}, /* & */ 283 | {0x34, 0}, /* ' */ 284 | {0x26, KEY_SHIFT}, /* ( */ 285 | {0x27, KEY_SHIFT}, /* ) */ 286 | {0x25, KEY_SHIFT}, /* * */ 287 | {0x2e, KEY_SHIFT}, /* + */ 288 | {0x36, 0}, /* , */ 289 | {0x2d, 0}, /* - */ 290 | {0x37, 0}, /* . */ 291 | {0x38, 0}, /* / */ 292 | {0x27, 0}, /* 0 */ 293 | {0x1e, 0}, /* 1 */ 294 | {0x1f, 0}, /* 2 */ 295 | {0x20, 0}, /* 3 */ 296 | {0x21, 0}, /* 4 */ 297 | {0x22, 0}, /* 5 */ 298 | {0x23, 0}, /* 6 */ 299 | {0x24, 0}, /* 7 */ 300 | {0x25, 0}, /* 8 */ 301 | {0x26, 0}, /* 9 */ 302 | {0x33, KEY_SHIFT}, /* : */ 303 | {0x33, 0}, /* ; */ 304 | {0x36, KEY_SHIFT}, /* < */ 305 | {0x2e, 0}, /* = */ 306 | {0x37, KEY_SHIFT}, /* > */ 307 | {0x38, KEY_SHIFT}, /* ? */ 308 | {0x34, KEY_SHIFT}, /* @ */ 309 | {0x04, KEY_SHIFT}, /* A */ 310 | {0x05, KEY_SHIFT}, /* B */ 311 | {0x06, KEY_SHIFT}, /* C */ 312 | {0x07, KEY_SHIFT}, /* D */ 313 | {0x08, KEY_SHIFT}, /* E */ 314 | {0x09, KEY_SHIFT}, /* F */ 315 | {0x0a, KEY_SHIFT}, /* G */ 316 | {0x0b, KEY_SHIFT}, /* H */ 317 | {0x0c, KEY_SHIFT}, /* I */ 318 | {0x0d, KEY_SHIFT}, /* J */ 319 | {0x0e, KEY_SHIFT}, /* K */ 320 | {0x0f, KEY_SHIFT}, /* L */ 321 | {0x10, KEY_SHIFT}, /* M */ 322 | {0x11, KEY_SHIFT}, /* N */ 323 | {0x12, KEY_SHIFT}, /* O */ 324 | {0x13, KEY_SHIFT}, /* P */ 325 | {0x14, KEY_SHIFT}, /* Q */ 326 | {0x15, KEY_SHIFT}, /* R */ 327 | {0x16, KEY_SHIFT}, /* S */ 328 | {0x17, KEY_SHIFT}, /* T */ 329 | {0x18, KEY_SHIFT}, /* U */ 330 | {0x19, KEY_SHIFT}, /* V */ 331 | {0x1a, KEY_SHIFT}, /* W */ 332 | {0x1b, KEY_SHIFT}, /* X */ 333 | {0x1c, KEY_SHIFT}, /* Y */ 334 | {0x1d, KEY_SHIFT}, /* Z */ 335 | {0x2f, 0}, /* [ */ 336 | {0x64, 0}, /* \ */ 337 | {0x30, 0}, /* ] */ 338 | {0x23, KEY_SHIFT}, /* ^ */ 339 | {0x2d, KEY_SHIFT}, /* _ */ 340 | {0x35, 0}, /* ` */ 341 | {0x04, 0}, /* a */ 342 | {0x05, 0}, /* b */ 343 | {0x06, 0}, /* c */ 344 | {0x07, 0}, /* d */ 345 | {0x08, 0}, /* e */ 346 | {0x09, 0}, /* f */ 347 | {0x0a, 0}, /* g */ 348 | {0x0b, 0}, /* h */ 349 | {0x0c, 0}, /* i */ 350 | {0x0d, 0}, /* j */ 351 | {0x0e, 0}, /* k */ 352 | {0x0f, 0}, /* l */ 353 | {0x10, 0}, /* m */ 354 | {0x11, 0}, /* n */ 355 | {0x12, 0}, /* o */ 356 | {0x13, 0}, /* p */ 357 | {0x14, 0}, /* q */ 358 | {0x15, 0}, /* r */ 359 | {0x16, 0}, /* s */ 360 | {0x17, 0}, /* t */ 361 | {0x18, 0}, /* u */ 362 | {0x19, 0}, /* v */ 363 | {0x1a, 0}, /* w */ 364 | {0x1b, 0}, /* x */ 365 | {0x1c, 0}, /* y */ 366 | {0x1d, 0}, /* z */ 367 | {0x2f, KEY_SHIFT}, /* { */ 368 | {0x64, KEY_SHIFT}, /* | */ 369 | {0x30, KEY_SHIFT}, /* } */ 370 | {0x32, KEY_SHIFT}, /* ~ */ 371 | {0,0}, /* DEL */ 372 | 373 | {0x3a, 0}, /* F1 */ 374 | {0x3b, 0}, /* F2 */ 375 | {0x3c, 0}, /* F3 */ 376 | {0x3d, 0}, /* F4 */ 377 | {0x3e, 0}, /* F5 */ 378 | {0x3f, 0}, /* F6 */ 379 | {0x40, 0}, /* F7 */ 380 | {0x41, 0}, /* F8 */ 381 | {0x42, 0}, /* F9 */ 382 | {0x43, 0}, /* F10 */ 383 | {0x44, 0}, /* F11 */ 384 | {0x45, 0}, /* F12 */ 385 | 386 | {0x46, 0}, /* PRINT_SCREEN */ 387 | {0x47, 0}, /* SCROLL_LOCK */ 388 | {0x39, 0}, /* CAPS_LOCK */ 389 | {0x53, 0}, /* NUM_LOCK */ 390 | {0x49, 0}, /* INSERT */ 391 | {0x4a, 0}, /* HOME */ 392 | {0x4b, 0}, /* PAGE_UP */ 393 | {0x4e, 0}, /* PAGE_DOWN */ 394 | 395 | {0x4f, 0}, /* RIGHT_ARROW */ 396 | {0x50, 0}, /* LEFT_ARROW */ 397 | {0x51, 0}, /* DOWN_ARROW */ 398 | {0x52, 0}, /* UP_ARROW */ 399 | }; 400 | #endif 401 | 402 | #endif 403 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------