Set ESP-IDF to 3.2 (#2662)
* Set IDF to v3.2 * Remove BLE submodule * Add BLE lib source * Update Camera example to support OV3660
This commit is contained in:
parent
14126060a1
commit
7b5cd47d07
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "libraries/BLE"]
|
||||
path = libraries/BLE
|
||||
url = https://github.com/nkolban/ESP32_BLE_Arduino.git
|
||||
[submodule "libraries/AzureIoT"]
|
||||
path = libraries/AzureIoT
|
||||
url = https://github.com/VSChina/ESP32_AzureIoT_Arduino
|
||||
|
52
boards.txt
52
boards.txt
@ -9,31 +9,7 @@ menu.PSRAM=PSRAM
|
||||
menu.Revision=Board Revision
|
||||
|
||||
##############################################################
|
||||
|
||||
esp32cam.name=AI Thinker ESP32-CAM
|
||||
|
||||
esp32cam.upload.tool=esptool_py
|
||||
esp32cam.upload.maximum_size=3145728
|
||||
esp32cam.upload.maximum_data_size=327680
|
||||
esp32cam.upload.wait_for_upload_port=true
|
||||
esp32cam.upload.speed=460800
|
||||
|
||||
esp32cam.serial.disableDTR=true
|
||||
esp32cam.serial.disableRTS=true
|
||||
|
||||
esp32cam.build.mcu=esp32
|
||||
esp32cam.build.core=esp32
|
||||
esp32cam.build.variant=esp32
|
||||
esp32cam.build.board=ESP32_DEV
|
||||
esp32cam.build.f_cpu=240000000L
|
||||
esp32cam.build.flash_size=4MB
|
||||
esp32cam.build.flash_freq=80m
|
||||
esp32cam.build.flash_mode=dio
|
||||
esp32cam.build.boot=qio
|
||||
esp32cam.build.partitions=huge_app
|
||||
esp32cam.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
|
||||
esp32cam.build.code_debug=0
|
||||
|
||||
### DO NOT PUT BOARDS ABOVE THE OFFICIAL ESPRESSIF BOARDS! ###
|
||||
##############################################################
|
||||
|
||||
esp32.name=ESP32 Dev Module
|
||||
@ -3449,4 +3425,30 @@ frogboard.menu.DebugLevel.verbose.build.code_debug=5
|
||||
|
||||
##############################################################
|
||||
|
||||
esp32cam.name=AI Thinker ESP32-CAM
|
||||
|
||||
esp32cam.upload.tool=esptool_py
|
||||
esp32cam.upload.maximum_size=3145728
|
||||
esp32cam.upload.maximum_data_size=327680
|
||||
esp32cam.upload.wait_for_upload_port=true
|
||||
esp32cam.upload.speed=460800
|
||||
|
||||
esp32cam.serial.disableDTR=true
|
||||
esp32cam.serial.disableRTS=true
|
||||
|
||||
esp32cam.build.mcu=esp32
|
||||
esp32cam.build.core=esp32
|
||||
esp32cam.build.variant=esp32
|
||||
esp32cam.build.board=ESP32_DEV
|
||||
esp32cam.build.f_cpu=240000000L
|
||||
esp32cam.build.flash_size=4MB
|
||||
esp32cam.build.flash_freq=80m
|
||||
esp32cam.build.flash_mode=dio
|
||||
esp32cam.build.boot=qio
|
||||
esp32cam.build.partitions=huge_app
|
||||
esp32cam.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
|
||||
esp32cam.build.code_debug=0
|
||||
|
||||
##############################################################
|
||||
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit b232e7f5f0e87f36afbc2f4e03a2c49c48dd47bc
|
15
libraries/BLE/README.md
Normal file
15
libraries/BLE/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# ESP32 BLE for Arduino
|
||||
The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino.
|
||||
|
||||
The actual source of the project which is being maintained can be found here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets
|
||||
|
||||
Issues and questions should be raised here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets/issues
|
||||
|
||||
|
||||
Documentation for using the library can be found here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets/tree/master/Documentation
|
160
libraries/BLE/examples/BLE_client/BLE_client.ino
Normal file
160
libraries/BLE/examples/BLE_client/BLE_client.ino
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* A BLE client example that is rich in capabilities.
|
||||
* There is a lot new capabilities implemented.
|
||||
* author unknown
|
||||
* updated by chegewara
|
||||
*/
|
||||
|
||||
#include "BLEDevice.h"
|
||||
//#include "BLEScan.h"
|
||||
|
||||
// The remote service we wish to connect to.
|
||||
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
|
||||
// The characteristic of the remote service we are interested in.
|
||||
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
|
||||
|
||||
static boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLERemoteCharacteristic* pRemoteCharacteristic;
|
||||
static BLEAdvertisedDevice* myDevice;
|
||||
|
||||
static void notifyCallback(
|
||||
BLERemoteCharacteristic* pBLERemoteCharacteristic,
|
||||
uint8_t* pData,
|
||||
size_t length,
|
||||
bool isNotify) {
|
||||
Serial.print("Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("data: ");
|
||||
Serial.println((char*)pData);
|
||||
}
|
||||
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient* pclient) {
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient* pclient) {
|
||||
connected = false;
|
||||
Serial.println("onDisconnect");
|
||||
}
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Forming a connection to ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
BLEClient* pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
pClient->setClientCallbacks(new MyClientCallback());
|
||||
|
||||
// Connect to the remove BLE Server.
|
||||
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
|
||||
Serial.println(" - Connected to server");
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server.
|
||||
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
|
||||
// Obtain a reference to the characteristic in the service of the remote BLE server.
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic");
|
||||
|
||||
// Read the value of the characteristic.
|
||||
if(pRemoteCharacteristic->canRead()) {
|
||||
std::string value = pRemoteCharacteristic->readValue();
|
||||
Serial.print("The characteristic value was: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
if(pRemoteCharacteristic->canNotify())
|
||||
pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
/**
|
||||
* Scan for BLE servers and find the first one that advertises the service we are looking for.
|
||||
*/
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
/**
|
||||
* Called for each advertising BLE server.
|
||||
*/
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Advertised Device found: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// We have found a device, let us now see if it contains the service we are looking for.
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
|
||||
|
||||
BLEDevice::getScan()->stop();
|
||||
myDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
doScan = true;
|
||||
|
||||
} // Found our server
|
||||
} // onResult
|
||||
}; // MyAdvertisedDeviceCallbacks
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Arduino BLE Client application...");
|
||||
BLEDevice::init("");
|
||||
|
||||
// Retrieve a Scanner and set the callback we want to use to be informed when we
|
||||
// have detected a new device. Specify that we want active scanning and start the
|
||||
// scan to run for 5 seconds.
|
||||
BLEScan* pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
} // End of setup.
|
||||
|
||||
|
||||
// This is the Arduino main loop function.
|
||||
void loop() {
|
||||
|
||||
// If the flag "doConnect" is true then we have scanned for and found the desired
|
||||
// BLE Server with which we wish to connect. Now we connect to it. Once we are
|
||||
// connected we set the connected flag to be true.
|
||||
if (doConnect == true) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("We are now connected to the BLE Server.");
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
|
||||
// with the current time since boot.
|
||||
if (connected) {
|
||||
String newValue = "Time since boot: " + String(millis()/1000);
|
||||
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
|
||||
|
||||
// Set the characteristic's value to be the array of bytes that is actually a string.
|
||||
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
|
||||
}else if(doScan){
|
||||
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
|
||||
}
|
||||
|
||||
delay(1000); // Delay a second between loops.
|
||||
} // End of loop
|
103
libraries/BLE/examples/BLE_iBeacon/BLE_iBeacon.ino
Normal file
103
libraries/BLE/examples/BLE_iBeacon/BLE_iBeacon.ino
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by pcbreflux
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic iBeacon frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
#include "sys/time.h"
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEBeacon.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint8_t temprature_sens_read();
|
||||
//uint8_t g_phyFuns;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval now;
|
||||
|
||||
#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/)
|
||||
|
||||
void setBeacon() {
|
||||
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
|
||||
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
|
||||
oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16);
|
||||
oBeacon.setMinor(bootcount&0xFFFF);
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
|
||||
|
||||
std::string strServiceData = "";
|
||||
|
||||
strServiceData += (char)26; // Len
|
||||
strServiceData += (char)0xFF; // Type
|
||||
strServiceData += oBeacon.getData();
|
||||
oAdvertisementData.addData(strServiceData);
|
||||
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
Serial.printf("start ESP32 %d\n",bootcount++);
|
||||
|
||||
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last);
|
||||
|
||||
last = now.tv_sec;
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("");
|
||||
|
||||
// Create the BLE Server
|
||||
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertizing started...");
|
||||
delay(100);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("enter deep sleep\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
Serial.printf("in deep sleep\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
110
libraries/BLE/examples/BLE_notify/BLE_notify.ino
Normal file
110
libraries/BLE/examples/BLE_notify/BLE_notify.ino
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect hander associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer* pServer = NULL;
|
||||
BLECharacteristic* pCharacteristic = NULL;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
40
libraries/BLE/examples/BLE_scan/BLE_scan.ino
Normal file
40
libraries/BLE/examples/BLE_scan/BLE_scan.ino
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan* pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices.getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
45
libraries/BLE/examples/BLE_server/BLE_server.ino
Normal file
45
libraries/BLE/examples/BLE_server/BLE_server.ino
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updates by chegewara
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
BLEDevice::init("Long name works now");
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pCharacteristic->setValue("Hello World says Neil");
|
||||
pService->start();
|
||||
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMinPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect hander associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer* pServer = NULL;
|
||||
BLECharacteristic* pCharacteristic = NULL;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
BLEDevice::startAdvertising();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
125
libraries/BLE/examples/BLE_uart/BLE_uart.ino
Normal file
125
libraries/BLE/examples/BLE_uart/BLE_uart.ino
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
|
||||
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
|
||||
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
In this example rxValue is the data received (only accessible inside that function).
|
||||
And txValue is the data to be sent, in this example just a byte incremented every second.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic * pTxCharacteristic;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint8_t txValue = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
|
||||
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("Received Value: ");
|
||||
for (int i = 0; i < rxValue.length(); i++)
|
||||
Serial.print(rxValue[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("UART Service");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pTxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_TX,
|
||||
BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
|
||||
pTxCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_RX,
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pRxCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
pServer->getAdvertising()->start();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (deviceConnected) {
|
||||
pTxCharacteristic->setValue(&txValue, 1);
|
||||
pTxCharacteristic->notify();
|
||||
txValue++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
|
||||
}
|
||||
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
65
libraries/BLE/examples/BLE_write/BLE_write.ino
Normal file
65
libraries/BLE/examples/BLE_write/BLE_write.ino
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string value = pCharacteristic->getValue();
|
||||
|
||||
if (value.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
Serial.print(value[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("1- Download and install an BLE scanner app in your phone");
|
||||
Serial.println("2- Scan for BLE devices in the app");
|
||||
Serial.println("3- Connect to MyESP32");
|
||||
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
|
||||
Serial.println("5- See the magic =)");
|
||||
|
||||
BLEDevice::init("MyESP32");
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
pCharacteristic->setValue("Hello World");
|
||||
pService->start();
|
||||
|
||||
BLEAdvertising *pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
10
libraries/BLE/library.properties
Normal file
10
libraries/BLE/library.properties
Normal file
@ -0,0 +1,10 @@
|
||||
name=ESP32 BLE Arduino
|
||||
version=1.0.1
|
||||
author=Neil Kolban <kolban1@kolban.com>
|
||||
maintainer=Dariusz Krempa <esp32@esp32.eu.org>
|
||||
sentence=BLE functions for ESP32
|
||||
paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform.
|
||||
category=Communication
|
||||
url=https://github.com/nkolban/ESP32_BLE_Arduino
|
||||
architectures=esp32
|
||||
includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h
|
62
libraries/BLE/src/BLE2902.cpp
Normal file
62
libraries/BLE/src/BLE2902.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* BLE2902.cpp
|
||||
*
|
||||
* Created on: Jun 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLE2902.h"
|
||||
|
||||
BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) {
|
||||
uint8_t data[2] = { 0, 0 };
|
||||
setValue(data, 2);
|
||||
} // BLE2902
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the notifications value.
|
||||
* @return The notifications value. True if notifications are enabled and false if not.
|
||||
*/
|
||||
bool BLE2902::getNotifications() {
|
||||
return (getValue()[0] & (1 << 0)) != 0;
|
||||
} // getNotifications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the indications value.
|
||||
* @return The indications value. True if indications are enabled and false if not.
|
||||
*/
|
||||
bool BLE2902::getIndications() {
|
||||
return (getValue()[0] & (1 << 1)) != 0;
|
||||
} // getIndications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the indications flag.
|
||||
* @param [in] flag The indications flag.
|
||||
*/
|
||||
void BLE2902::setIndications(bool flag) {
|
||||
uint8_t *pValue = getValue();
|
||||
if (flag) pValue[0] |= 1 << 1;
|
||||
else pValue[0] &= ~(1 << 1);
|
||||
} // setIndications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the notifications flag.
|
||||
* @param [in] flag The notifications flag.
|
||||
*/
|
||||
void BLE2902::setNotifications(bool flag) {
|
||||
uint8_t *pValue = getValue();
|
||||
if (flag) pValue[0] |= 1 << 0;
|
||||
else pValue[0] &= ~(1 << 0);
|
||||
} // setNotifications
|
||||
|
||||
#endif
|
34
libraries/BLE/src/BLE2902.h
Normal file
34
libraries/BLE/src/BLE2902.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* BLE2902.h
|
||||
*
|
||||
* Created on: Jun 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLE2902_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEDescriptor.h"
|
||||
|
||||
/**
|
||||
* @brief Descriptor for Client Characteristic Configuration.
|
||||
*
|
||||
* This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
*/
|
||||
class BLE2902: public BLEDescriptor {
|
||||
public:
|
||||
BLE2902();
|
||||
bool getNotifications();
|
||||
bool getIndications();
|
||||
void setNotifications(bool flag);
|
||||
void setIndications(bool flag);
|
||||
|
||||
}; // BLE2902
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */
|
74
libraries/BLE/src/BLE2904.cpp
Normal file
74
libraries/BLE/src/BLE2904.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* BLE2904.cpp
|
||||
*
|
||||
* Created on: Dec 23, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLE2904.h"
|
||||
|
||||
|
||||
BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) {
|
||||
m_data.m_format = 0;
|
||||
m_data.m_exponent = 0;
|
||||
m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers
|
||||
m_data.m_unit = 0;
|
||||
m_data.m_description = 0;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // BLE2902
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the description.
|
||||
*/
|
||||
void BLE2904::setDescription(uint16_t description) {
|
||||
m_data.m_description = description;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the exponent.
|
||||
*/
|
||||
void BLE2904::setExponent(int8_t exponent) {
|
||||
m_data.m_exponent = exponent;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setExponent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the format.
|
||||
*/
|
||||
void BLE2904::setFormat(uint8_t format) {
|
||||
m_data.m_format = format;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setFormat
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the namespace.
|
||||
*/
|
||||
void BLE2904::setNamespace(uint8_t namespace_value) {
|
||||
m_data.m_namespace = namespace_value;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setNamespace
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the units for this value. It should be one of the encoded values defined here:
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/units
|
||||
* @param [in] unit The type of units of this characteristic as defined by assigned numbers.
|
||||
*/
|
||||
void BLE2904::setUnit(uint16_t unit) {
|
||||
m_data.m_unit = unit;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setUnit
|
||||
|
||||
#endif
|
74
libraries/BLE/src/BLE2904.h
Normal file
74
libraries/BLE/src/BLE2904.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* BLE2904.h
|
||||
*
|
||||
* Created on: Dec 23, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLE2904_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEDescriptor.h"
|
||||
|
||||
struct BLE2904_Data {
|
||||
uint8_t m_format;
|
||||
int8_t m_exponent;
|
||||
uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units
|
||||
uint8_t m_namespace;
|
||||
uint16_t m_description;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Descriptor for Characteristic Presentation Format.
|
||||
*
|
||||
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
class BLE2904: public BLEDescriptor {
|
||||
public:
|
||||
BLE2904();
|
||||
static const uint8_t FORMAT_BOOLEAN = 1;
|
||||
static const uint8_t FORMAT_UINT2 = 2;
|
||||
static const uint8_t FORMAT_UINT4 = 3;
|
||||
static const uint8_t FORMAT_UINT8 = 4;
|
||||
static const uint8_t FORMAT_UINT12 = 5;
|
||||
static const uint8_t FORMAT_UINT16 = 6;
|
||||
static const uint8_t FORMAT_UINT24 = 7;
|
||||
static const uint8_t FORMAT_UINT32 = 8;
|
||||
static const uint8_t FORMAT_UINT48 = 9;
|
||||
static const uint8_t FORMAT_UINT64 = 10;
|
||||
static const uint8_t FORMAT_UINT128 = 11;
|
||||
static const uint8_t FORMAT_SINT8 = 12;
|
||||
static const uint8_t FORMAT_SINT12 = 13;
|
||||
static const uint8_t FORMAT_SINT16 = 14;
|
||||
static const uint8_t FORMAT_SINT24 = 15;
|
||||
static const uint8_t FORMAT_SINT32 = 16;
|
||||
static const uint8_t FORMAT_SINT48 = 17;
|
||||
static const uint8_t FORMAT_SINT64 = 18;
|
||||
static const uint8_t FORMAT_SINT128 = 19;
|
||||
static const uint8_t FORMAT_FLOAT32 = 20;
|
||||
static const uint8_t FORMAT_FLOAT64 = 21;
|
||||
static const uint8_t FORMAT_SFLOAT16 = 22;
|
||||
static const uint8_t FORMAT_SFLOAT32 = 23;
|
||||
static const uint8_t FORMAT_IEEE20601 = 24;
|
||||
static const uint8_t FORMAT_UTF8 = 25;
|
||||
static const uint8_t FORMAT_UTF16 = 26;
|
||||
static const uint8_t FORMAT_OPAQUE = 27;
|
||||
|
||||
void setDescription(uint16_t);
|
||||
void setExponent(int8_t exponent);
|
||||
void setFormat(uint8_t format);
|
||||
void setNamespace(uint8_t namespace_value);
|
||||
void setUnit(uint16_t unit);
|
||||
|
||||
private:
|
||||
BLE2904_Data m_data;
|
||||
}; // BLE2904
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */
|
95
libraries/BLE/src/BLEAddress.cpp
Normal file
95
libraries/BLE/src/BLEAddress.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* BLEAddress.cpp
|
||||
*
|
||||
* Created on: Jul 2, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEAddress.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create an address from the native ESP32 representation.
|
||||
* @param [in] address The native representation.
|
||||
*/
|
||||
BLEAddress::BLEAddress(esp_bd_addr_t address) {
|
||||
memcpy(m_address, address, ESP_BD_ADDR_LEN);
|
||||
} // BLEAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create an address from a hex string
|
||||
*
|
||||
* A hex string is of the format:
|
||||
* ```
|
||||
* 00:00:00:00:00:00
|
||||
* ```
|
||||
* which is 17 characters in length.
|
||||
*
|
||||
* @param [in] stringAddress The hex representation of the address.
|
||||
*/
|
||||
BLEAddress::BLEAddress(std::string stringAddress) {
|
||||
if (stringAddress.length() != 17) return;
|
||||
|
||||
int data[6];
|
||||
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
|
||||
m_address[0] = (uint8_t) data[0];
|
||||
m_address[1] = (uint8_t) data[1];
|
||||
m_address[2] = (uint8_t) data[2];
|
||||
m_address[3] = (uint8_t) data[3];
|
||||
m_address[4] = (uint8_t) data[4];
|
||||
m_address[5] = (uint8_t) data[5];
|
||||
} // BLEAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Determine if this address equals another.
|
||||
* @param [in] otherAddress The other address to compare against.
|
||||
* @return True if the addresses are equal.
|
||||
*/
|
||||
bool BLEAddress::equals(BLEAddress otherAddress) {
|
||||
return memcmp(otherAddress.getNative(), m_address, 6) == 0;
|
||||
} // equals
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the native representation of the address.
|
||||
* @return The native representation of the address.
|
||||
*/
|
||||
esp_bd_addr_t *BLEAddress::getNative() {
|
||||
return &m_address;
|
||||
} // getNative
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a BLE address to a string.
|
||||
*
|
||||
* A string representation of an address is in the format:
|
||||
*
|
||||
* ```
|
||||
* xx:xx:xx:xx:xx:xx
|
||||
* ```
|
||||
*
|
||||
* @return The string representation of the address.
|
||||
*/
|
||||
std::string BLEAddress::toString() {
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[0] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[1] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[2] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[3] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[4] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[5];
|
||||
return stream.str();
|
||||
} // toString
|
||||
#endif
|
34
libraries/BLE/src/BLEAddress.h
Normal file
34
libraries/BLE/src/BLEAddress.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* BLEAddress.h
|
||||
*
|
||||
* Created on: Jul 2, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* @brief A %BLE device address.
|
||||
*
|
||||
* Every %BLE device has a unique address which can be used to identify it and form connections.
|
||||
*/
|
||||
class BLEAddress {
|
||||
public:
|
||||
BLEAddress(esp_bd_addr_t address);
|
||||
BLEAddress(std::string stringAddress);
|
||||
bool equals(BLEAddress otherAddress);
|
||||
esp_bd_addr_t* getNative();
|
||||
std::string toString();
|
||||
|
||||
private:
|
||||
esp_bd_addr_t m_address;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */
|
529
libraries/BLE/src/BLEAdvertisedDevice.cpp
Normal file
529
libraries/BLE/src/BLEAdvertisedDevice.cpp
Normal file
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* BLEAdvertisedDevice.cpp
|
||||
*
|
||||
* During the scanning procedure, we will be finding advertised BLE devices. This class
|
||||
* models a found device.
|
||||
*
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
|
||||
*
|
||||
* Created on: Jul 3, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG="BLEAdvertisedDevice";
|
||||
#endif
|
||||
|
||||
BLEAdvertisedDevice::BLEAdvertisedDevice() {
|
||||
m_adFlag = 0;
|
||||
m_appearance = 0;
|
||||
m_deviceType = 0;
|
||||
m_manufacturerData = "";
|
||||
m_name = "";
|
||||
m_rssi = -9999;
|
||||
m_serviceData = "";
|
||||
m_txPower = 0;
|
||||
m_pScan = nullptr;
|
||||
|
||||
m_haveAppearance = false;
|
||||
m_haveManufacturerData = false;
|
||||
m_haveName = false;
|
||||
m_haveRSSI = false;
|
||||
m_haveServiceData = false;
|
||||
m_haveServiceUUID = false;
|
||||
m_haveTXPower = false;
|
||||
|
||||
} // BLEAdvertisedDevice
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the address.
|
||||
*
|
||||
* Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
|
||||
* Call this function to obtain the address of the advertised device.
|
||||
*
|
||||
* @return The address of the advertised device.
|
||||
*/
|
||||
BLEAddress BLEAdvertisedDevice::getAddress() {
|
||||
return m_address;
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the appearance.
|
||||
*
|
||||
* A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user
|
||||
* typcially in the form of an icon.
|
||||
*
|
||||
* @return The appearance of the advertised device.
|
||||
*/
|
||||
uint16_t BLEAdvertisedDevice::getAppearance() {
|
||||
return m_appearance;
|
||||
} // getAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the manufacturer data.
|
||||
* @return The manufacturer data of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getManufacturerData() {
|
||||
return m_manufacturerData;
|
||||
} // getManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the name.
|
||||
* @return The name of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getName() {
|
||||
return m_name;
|
||||
} // getName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the RSSI.
|
||||
* @return The RSSI of the advertised device.
|
||||
*/
|
||||
int BLEAdvertisedDevice::getRSSI() {
|
||||
return m_rssi;
|
||||
} // getRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the scan object that created this advertisement.
|
||||
* @return The scan object.
|
||||
*/
|
||||
BLEScan* BLEAdvertisedDevice::getScan() {
|
||||
return m_pScan;
|
||||
} // getScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service data.
|
||||
* @return The ServiceData of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getServiceData() {
|
||||
return m_serviceData;
|
||||
} //getServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service data UUID.
|
||||
* @return The service data UUID.
|
||||
*/
|
||||
BLEUUID BLEAdvertisedDevice::getServiceDataUUID() {
|
||||
return m_serviceDataUUID;
|
||||
} // getServiceDataUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the Service UUID.
|
||||
* @return The Service UUID of the advertised device.
|
||||
*/
|
||||
BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
|
||||
return m_serviceUUIDs[0];
|
||||
} // getServiceUUID
|
||||
|
||||
/**
|
||||
* @brief Check advertised serviced for existence required UUID
|
||||
* @return Return true if service is advertised
|
||||
*/
|
||||
bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){
|
||||
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
|
||||
if (m_serviceUUIDs[i].equals(uuid)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the TX Power.
|
||||
* @return The TX Power of the advertised device.
|
||||
*/
|
||||
int8_t BLEAdvertisedDevice::getTXPower() {
|
||||
return m_txPower;
|
||||
} // getTXPower
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have an appearance value?
|
||||
* @return True if there is an appearance value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveAppearance() {
|
||||
return m_haveAppearance;
|
||||
} // haveAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have manufacturer data?
|
||||
* @return True if there is manufacturer data present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveManufacturerData() {
|
||||
return m_haveManufacturerData;
|
||||
} // haveManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a name value?
|
||||
* @return True if there is a name value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveName() {
|
||||
return m_haveName;
|
||||
} // haveName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a signal strength value?
|
||||
* @return True if there is a signal strength value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveRSSI() {
|
||||
return m_haveRSSI;
|
||||
} // haveRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service data value?
|
||||
* @return True if there is a service data value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveServiceData() {
|
||||
return m_haveServiceData;
|
||||
} // haveServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service UUID value?
|
||||
* @return True if there is a service UUID value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveServiceUUID() {
|
||||
return m_haveServiceUUID;
|
||||
} // haveServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a transmission power value?
|
||||
* @return True if there is a transmission power value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveTXPower() {
|
||||
return m_haveTXPower;
|
||||
} // haveTXPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parse the advertising pay load.
|
||||
*
|
||||
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by
|
||||
* a 0 length value. Each entry in the buffer has the format:
|
||||
* [length][type][data...]
|
||||
*
|
||||
* The length does not include itself but does include everything after it until the next record. A record
|
||||
* with a length value of 0 indicates a terminator.
|
||||
*
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
|
||||
*/
|
||||
void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) {
|
||||
uint8_t length;
|
||||
uint8_t ad_type;
|
||||
uint8_t sizeConsumed = 0;
|
||||
bool finished = false;
|
||||
m_payload = payload;
|
||||
m_payloadLength = total_len;
|
||||
|
||||
while(!finished) {
|
||||
length = *payload; // Retrieve the length of the record.
|
||||
payload++; // Skip to type
|
||||
sizeConsumed += 1 + length; // increase the size consumed.
|
||||
|
||||
if (length != 0) { // A length of 0 indicates that we have reached the end.
|
||||
ad_type = *payload;
|
||||
payload++;
|
||||
length--;
|
||||
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, payload, length);
|
||||
ESP_LOGD(LOG_TAG, "Type: 0x%.2x (%s), length: %d, data: %s",
|
||||
ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
|
||||
free(pHex);
|
||||
|
||||
switch(ad_type) {
|
||||
case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
|
||||
setName(std::string(reinterpret_cast<char*>(payload), length));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_NAME_CMPL
|
||||
|
||||
case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
|
||||
setTXPower(*payload);
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_TX_PWR
|
||||
|
||||
case ESP_BLE_AD_TYPE_APPEARANCE: { // Adv Data Type: 0x19
|
||||
setAppearance(*reinterpret_cast<uint16_t*>(payload));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_APPEARANCE
|
||||
|
||||
case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
|
||||
setAdFlag(*payload);
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_FLAG
|
||||
|
||||
case ESP_BLE_AD_TYPE_16SRV_CMPL:
|
||||
case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
|
||||
for (int var = 0; var < length/2; ++var) {
|
||||
setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t*>(payload + var * 2)));
|
||||
}
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_16SRV_PART
|
||||
|
||||
case ESP_BLE_AD_TYPE_32SRV_CMPL:
|
||||
case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
|
||||
for (int var = 0; var < length/4; ++var) {
|
||||
setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t*>(payload + var * 4)));
|
||||
}
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_32SRV_PART
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SRV_CMPL: { // Adv Data Type: 0x07
|
||||
setServiceUUID(BLEUUID(payload, 16, false));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_128SRV_CMPL
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SRV_PART: { // Adv Data Type: 0x06
|
||||
setServiceUUID(BLEUUID(payload, 16, false));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_128SRV_PART
|
||||
|
||||
// See CSS Part A 1.4 Manufacturer Specific Data
|
||||
case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
|
||||
setManufacturerData(std::string(reinterpret_cast<char*>(payload), length));
|
||||
break;
|
||||
} // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE
|
||||
|
||||
case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID
|
||||
if (length < 2) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
uint16_t uuid = *(uint16_t*)payload;
|
||||
setServiceDataUUID(BLEUUID(uuid));
|
||||
if (length > 2) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 2), length - 2));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_SERVICE_DATA
|
||||
|
||||
case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID
|
||||
if (length < 4) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
uint32_t uuid = *(uint32_t*) payload;
|
||||
setServiceDataUUID(BLEUUID(uuid));
|
||||
if (length > 4) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 4), length - 4));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID
|
||||
if (length < 16) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
|
||||
setServiceDataUUID(BLEUUID(payload, (size_t)16, false));
|
||||
if (length > 16) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 16), length - 16));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
|
||||
|
||||
default: {
|
||||
ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type);
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
payload += length;
|
||||
} // Length <> 0
|
||||
|
||||
|
||||
if (sizeConsumed >= total_len)
|
||||
finished = true;
|
||||
|
||||
} // !finished
|
||||
} // parseAdvertisement
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the address of the advertised device.
|
||||
* @param [in] address The address of the advertised device.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAddress(BLEAddress address) {
|
||||
m_address = address;
|
||||
} // setAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the adFlag for this device.
|
||||
* @param [in] The discovered adFlag.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAdFlag(uint8_t adFlag) {
|
||||
m_adFlag = adFlag;
|
||||
} // setAdFlag
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the appearance for this device.
|
||||
* @param [in] The discovered appearance.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAppearance(uint16_t appearance) {
|
||||
m_appearance = appearance;
|
||||
m_haveAppearance = true;
|
||||
ESP_LOGD(LOG_TAG, "- appearance: %d", m_appearance);
|
||||
} // setAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the manufacturer data for this device.
|
||||
* @param [in] The discovered manufacturer data.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
|
||||
m_manufacturerData = manufacturerData;
|
||||
m_haveManufacturerData = true;
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
|
||||
ESP_LOGD(LOG_TAG, "- manufacturer data: %s", pHex);
|
||||
free(pHex);
|
||||
} // setManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the name for this device.
|
||||
* @param [in] name The discovered name.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setName(std::string name) {
|
||||
m_name = name;
|
||||
m_haveName = true;
|
||||
ESP_LOGD(LOG_TAG, "- setName(): name: %s", m_name.c_str());
|
||||
} // setName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the RSSI for this device.
|
||||
* @param [in] rssi The discovered RSSI.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setRSSI(int rssi) {
|
||||
m_rssi = rssi;
|
||||
m_haveRSSI = true;
|
||||
ESP_LOGD(LOG_TAG, "- setRSSI(): rssi: %d", m_rssi);
|
||||
} // setRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Scan that created this advertised device.
|
||||
* @param pScan The Scan that created this advertised device.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setScan(BLEScan* pScan) {
|
||||
m_pScan = pScan;
|
||||
} // setScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Service UUID for this device.
|
||||
* @param [in] serviceUUID The discovered serviceUUID
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
|
||||
return setServiceUUID(BLEUUID(serviceUUID));
|
||||
} // setServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Service UUID for this device.
|
||||
* @param [in] serviceUUID The discovered serviceUUID
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
|
||||
m_serviceUUIDs.push_back(serviceUUID);
|
||||
m_haveServiceUUID = true;
|
||||
ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
|
||||
} // setServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the ServiceData value.
|
||||
* @param [in] data ServiceData value.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceData(std::string serviceData) {
|
||||
m_haveServiceData = true; // Set the flag that indicates we have service data.
|
||||
m_serviceData = serviceData; // Save the service data that we received.
|
||||
} //setServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the ServiceDataUUID value.
|
||||
* @param [in] data ServiceDataUUID value.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) {
|
||||
m_haveServiceData = true; // Set the flag that indicates we have service data.
|
||||
m_serviceDataUUID = uuid;
|
||||
} // setServiceDataUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the power level for this device.
|
||||
* @param [in] txPower The discovered power level.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setTXPower(int8_t txPower) {
|
||||
m_txPower = txPower;
|
||||
m_haveTXPower = true;
|
||||
ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower);
|
||||
} // setTXPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a string representation of this device.
|
||||
* @return A string representation of this device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::toString() {
|
||||
std::stringstream ss;
|
||||
ss << "Name: " << getName() << ", Address: " << getAddress().toString();
|
||||
if (haveAppearance()) {
|
||||
ss << ", appearance: " << getAppearance();
|
||||
}
|
||||
if (haveManufacturerData()) {
|
||||
char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length());
|
||||
ss << ", manufacturer data: " << pHex;
|
||||
free(pHex);
|
||||
}
|
||||
if (haveServiceUUID()) {
|
||||
ss << ", serviceUUID: " << getServiceUUID().toString();
|
||||
}
|
||||
if (haveTXPower()) {
|
||||
ss << ", txPower: " << (int)getTXPower();
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
uint8_t* BLEAdvertisedDevice::getPayload() {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() {
|
||||
return m_addressType;
|
||||
}
|
||||
|
||||
void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) {
|
||||
m_addressType = type;
|
||||
}
|
||||
|
||||
size_t BLEAdvertisedDevice::getPayloadLength() {
|
||||
return m_payloadLength;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
123
libraries/BLE/src/BLEAdvertisedDevice.h
Normal file
123
libraries/BLE/src/BLEAdvertisedDevice.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* BLEAdvertisedDevice.h
|
||||
*
|
||||
* Created on: Jul 3, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BLEAddress.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEUUID.h"
|
||||
|
||||
|
||||
class BLEScan;
|
||||
/**
|
||||
* @brief A representation of a %BLE advertised device found by a scan.
|
||||
*
|
||||
* When we perform a %BLE scan, the result will be a set of devices that are advertising. This
|
||||
* class provides a model of a detected device.
|
||||
*/
|
||||
class BLEAdvertisedDevice {
|
||||
public:
|
||||
BLEAdvertisedDevice();
|
||||
|
||||
BLEAddress getAddress();
|
||||
uint16_t getAppearance();
|
||||
std::string getManufacturerData();
|
||||
std::string getName();
|
||||
int getRSSI();
|
||||
BLEScan* getScan();
|
||||
std::string getServiceData();
|
||||
BLEUUID getServiceDataUUID();
|
||||
BLEUUID getServiceUUID();
|
||||
int8_t getTXPower();
|
||||
uint8_t* getPayload();
|
||||
size_t getPayloadLength();
|
||||
esp_ble_addr_type_t getAddressType();
|
||||
void setAddressType(esp_ble_addr_type_t type);
|
||||
|
||||
|
||||
bool isAdvertisingService(BLEUUID uuid);
|
||||
bool haveAppearance();
|
||||
bool haveManufacturerData();
|
||||
bool haveName();
|
||||
bool haveRSSI();
|
||||
bool haveServiceData();
|
||||
bool haveServiceUUID();
|
||||
bool haveTXPower();
|
||||
|
||||
std::string toString();
|
||||
|
||||
private:
|
||||
friend class BLEScan;
|
||||
|
||||
void parseAdvertisement(uint8_t* payload, size_t total_len=62);
|
||||
void setAddress(BLEAddress address);
|
||||
void setAdFlag(uint8_t adFlag);
|
||||
void setAdvertizementResult(uint8_t* payload);
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setManufacturerData(std::string manufacturerData);
|
||||
void setName(std::string name);
|
||||
void setRSSI(int rssi);
|
||||
void setScan(BLEScan* pScan);
|
||||
void setServiceData(std::string data);
|
||||
void setServiceDataUUID(BLEUUID uuid);
|
||||
void setServiceUUID(const char* serviceUUID);
|
||||
void setServiceUUID(BLEUUID serviceUUID);
|
||||
void setTXPower(int8_t txPower);
|
||||
|
||||
bool m_haveAppearance;
|
||||
bool m_haveManufacturerData;
|
||||
bool m_haveName;
|
||||
bool m_haveRSSI;
|
||||
bool m_haveServiceData;
|
||||
bool m_haveServiceUUID;
|
||||
bool m_haveTXPower;
|
||||
|
||||
|
||||
BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0");
|
||||
uint8_t m_adFlag;
|
||||
uint16_t m_appearance;
|
||||
int m_deviceType;
|
||||
std::string m_manufacturerData;
|
||||
std::string m_name;
|
||||
BLEScan* m_pScan;
|
||||
int m_rssi;
|
||||
std::vector<BLEUUID> m_serviceUUIDs;
|
||||
int8_t m_txPower;
|
||||
std::string m_serviceData;
|
||||
BLEUUID m_serviceDataUUID;
|
||||
uint8_t* m_payload;
|
||||
size_t m_payloadLength = 0;
|
||||
esp_ble_addr_type_t m_addressType;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A callback handler for callbacks associated device scanning.
|
||||
*
|
||||
* When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
|
||||
* has been found. This class can be sub-classed and registered such that when a scan is performed and
|
||||
* a new advertised device has been found, we will be called back to be notified.
|
||||
*/
|
||||
class BLEAdvertisedDeviceCallbacks {
|
||||
public:
|
||||
virtual ~BLEAdvertisedDeviceCallbacks() {}
|
||||
/**
|
||||
* @brief Called when a new scan result is detected.
|
||||
*
|
||||
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
|
||||
* device that was found. During any individual scan, a device will only be detected one time.
|
||||
*/
|
||||
virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */
|
505
libraries/BLE/src/BLEAdvertising.cpp
Normal file
505
libraries/BLE/src/BLEAdvertising.cpp
Normal file
@ -0,0 +1,505 @@
|
||||
/*
|
||||
* BLEAdvertising.cpp
|
||||
*
|
||||
* This class encapsulates advertising a BLE Server.
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*
|
||||
* The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set
|
||||
* of properties that are advertised and has built a data structure that can be populated by the programmer.
|
||||
* This means that the programmer doesn't have to "mess with" the low level construction of a low level
|
||||
* BLE advertising frame. Many of the fields are determined for us while others we can set before starting
|
||||
* to advertise.
|
||||
*
|
||||
* Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters
|
||||
* upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer
|
||||
* set in the data will be advertised.
|
||||
*
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include "BLEAdvertising.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEAdvertising";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a default advertising object.
|
||||
*
|
||||
*/
|
||||
BLEAdvertising::BLEAdvertising() {
|
||||
m_advData.set_scan_rsp = false;
|
||||
m_advData.include_name = true;
|
||||
m_advData.include_txpower = true;
|
||||
m_advData.min_interval = 0x20;
|
||||
m_advData.max_interval = 0x40;
|
||||
m_advData.appearance = 0x00;
|
||||
m_advData.manufacturer_len = 0;
|
||||
m_advData.p_manufacturer_data = nullptr;
|
||||
m_advData.service_data_len = 0;
|
||||
m_advData.p_service_data = nullptr;
|
||||
m_advData.service_uuid_len = 0;
|
||||
m_advData.p_service_uuid = nullptr;
|
||||
m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
|
||||
|
||||
m_advParams.adv_int_min = 0x20;
|
||||
m_advParams.adv_int_max = 0x40;
|
||||
m_advParams.adv_type = ADV_TYPE_IND;
|
||||
m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
m_advParams.channel_map = ADV_CHNL_ALL;
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
|
||||
m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
|
||||
m_customAdvData = false; // No custom advertising data
|
||||
m_customScanResponseData = false; // No custom scan response data
|
||||
} // BLEAdvertising
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a service uuid to exposed list of services.
|
||||
* @param [in] serviceUUID The UUID of the service to expose.
|
||||
*/
|
||||
void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) {
|
||||
m_serviceUUIDs.push_back(serviceUUID);
|
||||
} // addServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a service uuid to exposed list of services.
|
||||
* @param [in] serviceUUID The string representation of the service to expose.
|
||||
*/
|
||||
void BLEAdvertising::addServiceUUID(const char* serviceUUID) {
|
||||
addServiceUUID(BLEUUID(serviceUUID));
|
||||
} // addServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the device appearance in the advertising data.
|
||||
* The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
|
||||
* @param [in] appearance The appearance of the device in the advertising data.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::setAppearance(uint16_t appearance) {
|
||||
m_advData.appearance = appearance;
|
||||
} // setAppearance
|
||||
|
||||
void BLEAdvertising::setMinInterval(uint16_t mininterval) {
|
||||
m_advParams.adv_int_min = mininterval;
|
||||
} // setMinInterval
|
||||
|
||||
void BLEAdvertising::setMaxInterval(uint16_t maxinterval) {
|
||||
m_advParams.adv_int_max = maxinterval;
|
||||
} // setMaxInterval
|
||||
|
||||
void BLEAdvertising::setMinPreferred(uint16_t mininterval) {
|
||||
m_advData.min_interval = mininterval;
|
||||
} //
|
||||
|
||||
void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
|
||||
m_advData.max_interval = maxinterval;
|
||||
} //
|
||||
|
||||
void BLEAdvertising::setScanResponse(bool set) {
|
||||
m_scanResp = set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the filtering for the scan filter.
|
||||
* @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
|
||||
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
|
||||
*/
|
||||
void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
|
||||
ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
|
||||
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
} // setScanFilter
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement data that is to be published in a regular advertisement.
|
||||
* @param [in] advertisementData The data to be advertised.
|
||||
*/
|
||||
void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) {
|
||||
ESP_LOGD(LOG_TAG, ">> setAdvertisementData");
|
||||
esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw(
|
||||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
|
||||
ESP_LOGD(LOG_TAG, "<< setAdvertisementData");
|
||||
} // setAdvertisementData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement data that is to be published in a scan response.
|
||||
* @param [in] advertisementData The data to be advertised.
|
||||
*/
|
||||
void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) {
|
||||
ESP_LOGD(LOG_TAG, ">> setScanResponseData");
|
||||
esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw(
|
||||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
|
||||
ESP_LOGD(LOG_TAG, "<< setScanResponseData");
|
||||
} // setScanResponseData
|
||||
|
||||
/**
|
||||
* @brief Start advertising.
|
||||
* Start advertising.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::start() {
|
||||
ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
|
||||
|
||||
// We have a vector of service UUIDs that we wish to advertise. In order to use the
|
||||
// ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte)
|
||||
// representations. If we have 1 or more services to advertise then we allocate enough
|
||||
// storage to host them and then copy them in one at a time into the contiguous storage.
|
||||
int numServices = m_serviceUUIDs.size();
|
||||
if (numServices > 0) {
|
||||
m_advData.service_uuid_len = 16 * numServices;
|
||||
m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len];
|
||||
uint8_t* p = m_advData.p_service_uuid;
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str());
|
||||
BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128();
|
||||
memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16);
|
||||
p += 16;
|
||||
}
|
||||
} else {
|
||||
m_advData.service_uuid_len = 0;
|
||||
ESP_LOGD(LOG_TAG, "- no services advertised");
|
||||
}
|
||||
|
||||
esp_err_t errRc;
|
||||
|
||||
if (!m_customAdvData) {
|
||||
// Set the configuration for advertising.
|
||||
m_advData.set_scan_rsp = false;
|
||||
m_advData.include_name = !m_scanResp;
|
||||
m_advData.include_txpower = !m_scanResp;
|
||||
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_customScanResponseData && m_scanResp) {
|
||||
m_advData.set_scan_rsp = true;
|
||||
m_advData.include_name = m_scanResp;
|
||||
m_advData.include_txpower = m_scanResp;
|
||||
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we had services to advertise then we previously allocated some storage for them.
|
||||
// Here we release that storage.
|
||||
if (m_advData.service_uuid_len > 0) {
|
||||
delete[] m_advData.p_service_uuid;
|
||||
m_advData.p_service_uuid = nullptr;
|
||||
}
|
||||
|
||||
// Start advertising.
|
||||
errRc = ::esp_ble_gap_start_advertising(&m_advParams);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< start");
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stop advertising.
|
||||
* Stop advertising.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::stop() {
|
||||
ESP_LOGD(LOG_TAG, ">> stop");
|
||||
esp_err_t errRc = ::esp_ble_gap_stop_advertising();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< stop");
|
||||
} // stop
|
||||
|
||||
/**
|
||||
* @brief Add data to the payload to be advertised.
|
||||
* @param [in] data The data to be added to the payload.
|
||||
*/
|
||||
void BLEAdvertisementData::addData(std::string data) {
|
||||
if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) {
|
||||
return;
|
||||
}
|
||||
m_payload.append(data);
|
||||
} // addData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the appearance.
|
||||
* @param [in] appearance The appearance code value.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
*/
|
||||
void BLEAdvertisementData::setAppearance(uint16_t appearance) {
|
||||
char cdata[2];
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19
|
||||
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
|
||||
} // setAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the complete services.
|
||||
* @param [in] uuid The single service to advertise.
|
||||
*/
|
||||
void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x02] [LL] [HH]
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x04] [LL] [LL] [HH] [HH]
|
||||
cdata[0] = 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x04] [0] [1] ... [15]
|
||||
cdata[0] = 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07
|
||||
addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setCompleteServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement flags.
|
||||
* @param [in] The flags to be set in the advertisement.
|
||||
*
|
||||
* * ESP_BLE_ADV_FLAG_LIMIT_DISC
|
||||
* * ESP_BLE_ADV_FLAG_GEN_DISC
|
||||
* * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
|
||||
* * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
|
||||
* * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
|
||||
* * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
|
||||
*/
|
||||
void BLEAdvertisementData::setFlags(uint8_t flag) {
|
||||
char cdata[3];
|
||||
cdata[0] = 2;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01
|
||||
cdata[2] = flag;
|
||||
addData(std::string(cdata, 3));
|
||||
} // setFlag
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set manufacturer specific data.
|
||||
* @param [in] data Manufacturer data.
|
||||
*/
|
||||
void BLEAdvertisementData::setManufacturerData(std::string data) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData");
|
||||
char cdata[2];
|
||||
cdata[0] = data.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff
|
||||
addData(std::string(cdata, 2) + data);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData");
|
||||
} // setManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the name.
|
||||
* @param [in] The complete name of the device.
|
||||
*/
|
||||
void BLEAdvertisementData::setName(std::string name) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str());
|
||||
char cdata[2];
|
||||
cdata[0] = name.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09
|
||||
addData(std::string(cdata, 2) + name);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setName");
|
||||
} // setName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the partial services.
|
||||
* @param [in] uuid The single service to advertise.
|
||||
*/
|
||||
void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x02] [LL] [HH]
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x04] [LL] [LL] [HH] [HH]
|
||||
cdata[0] = 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x04] [0] [1] ... [15]
|
||||
cdata[0] = 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setPartialServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the service data (UUID + data)
|
||||
* @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
|
||||
* @param [in] data The data to be associated with the service data advert.
|
||||
*/
|
||||
void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x16] [UUID16] data
|
||||
cdata[0] = data.length() + 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x20] [UUID32] data
|
||||
cdata[0] = data.length() + 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x21] [UUID128] data
|
||||
cdata[0] = data.length() + 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the short name.
|
||||
* @param [in] The short name of the device.
|
||||
*/
|
||||
void BLEAdvertisementData::setShortName(std::string name) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str());
|
||||
char cdata[2];
|
||||
cdata[0] = name.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08
|
||||
addData(std::string(cdata, 2) + name);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setShortName");
|
||||
} // setShortName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the payload that is to be advertised.
|
||||
* @return The payload that is to be advertised.
|
||||
*/
|
||||
std::string BLEAdvertisementData::getPayload() {
|
||||
return m_payload;
|
||||
} // getPayload
|
||||
|
||||
void BLEAdvertising::handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event);
|
||||
|
||||
switch(event) {
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: {
|
||||
ESP_LOGI(LOG_TAG, "STOP advertising");
|
||||
start();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
78
libraries/BLE/src/BLEAdvertising.h
Normal file
78
libraries/BLE/src/BLEAdvertising.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* BLEAdvertising.h
|
||||
*
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include "BLEUUID.h"
|
||||
#include <vector>
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
/**
|
||||
* @brief Advertisement data set by the programmer to be published by the %BLE server.
|
||||
*/
|
||||
class BLEAdvertisementData {
|
||||
// Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will
|
||||
// be exposed on demand/request or as time permits.
|
||||
//
|
||||
public:
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setCompleteServices(BLEUUID uuid);
|
||||
void setFlags(uint8_t);
|
||||
void setManufacturerData(std::string data);
|
||||
void setName(std::string name);
|
||||
void setPartialServices(BLEUUID uuid);
|
||||
void setServiceData(BLEUUID uuid, std::string data);
|
||||
void setShortName(std::string name);
|
||||
void addData(std::string data); // Add data to the payload.
|
||||
std::string getPayload(); // Retrieve the current advert payload.
|
||||
|
||||
private:
|
||||
friend class BLEAdvertising;
|
||||
std::string m_payload; // The payload of the advertisement.
|
||||
}; // BLEAdvertisementData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Perform and manage %BLE advertising.
|
||||
*
|
||||
* A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
|
||||
*/
|
||||
class BLEAdvertising {
|
||||
public:
|
||||
BLEAdvertising();
|
||||
void addServiceUUID(BLEUUID serviceUUID);
|
||||
void addServiceUUID(const char* serviceUUID);
|
||||
void start();
|
||||
void stop();
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setMaxInterval(uint16_t maxinterval);
|
||||
void setMinInterval(uint16_t mininterval);
|
||||
void setAdvertisementData(BLEAdvertisementData& advertisementData);
|
||||
void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
|
||||
void setScanResponseData(BLEAdvertisementData& advertisementData);
|
||||
void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
|
||||
|
||||
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
|
||||
void setMinPreferred(uint16_t);
|
||||
void setMaxPreferred(uint16_t);
|
||||
void setScanResponse(bool);
|
||||
|
||||
private:
|
||||
esp_ble_adv_data_t m_advData;
|
||||
esp_ble_adv_params_t m_advParams;
|
||||
std::vector<BLEUUID> m_serviceUUIDs;
|
||||
bool m_customAdvData = false; // Are we using custom advertising data?
|
||||
bool m_customScanResponseData = false; // Are we using custom scan response data?
|
||||
FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert");
|
||||
bool m_scanResp = true;
|
||||
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */
|
89
libraries/BLE/src/BLEBeacon.cpp
Normal file
89
libraries/BLE/src/BLEBeacon.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* BLEBeacon.cpp
|
||||
*
|
||||
* Created on: Jan 4, 2018
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include "BLEBeacon.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEBeacon";
|
||||
#endif
|
||||
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
|
||||
|
||||
|
||||
BLEBeacon::BLEBeacon() {
|
||||
m_beaconData.manufacturerId = 0x4c00;
|
||||
m_beaconData.subType = 0x02;
|
||||
m_beaconData.subTypeLength = 0x15;
|
||||
m_beaconData.major = 0;
|
||||
m_beaconData.minor = 0;
|
||||
m_beaconData.signalPower = 0;
|
||||
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
|
||||
} // BLEBeacon
|
||||
|
||||
std::string BLEBeacon::getData() {
|
||||
return std::string((char*) &m_beaconData, sizeof(m_beaconData));
|
||||
} // getData
|
||||
|
||||
uint16_t BLEBeacon::getMajor() {
|
||||
return m_beaconData.major;
|
||||
}
|
||||
|
||||
uint16_t BLEBeacon::getManufacturerId() {
|
||||
return m_beaconData.manufacturerId;
|
||||
}
|
||||
|
||||
uint16_t BLEBeacon::getMinor() {
|
||||
return m_beaconData.minor;
|
||||
}
|
||||
|
||||
BLEUUID BLEBeacon::getProximityUUID() {
|
||||
return BLEUUID(m_beaconData.proximityUUID, 16, false);
|
||||
}
|
||||
|
||||
int8_t BLEBeacon::getSignalPower() {
|
||||
return m_beaconData.signalPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEBeacon::setData(std::string data) {
|
||||
if (data.length() != sizeof(m_beaconData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData));
|
||||
return;
|
||||
}
|
||||
memcpy(&m_beaconData, data.data(), sizeof(m_beaconData));
|
||||
} // setData
|
||||
|
||||
void BLEBeacon::setMajor(uint16_t major) {
|
||||
m_beaconData.major = ENDIAN_CHANGE_U16(major);
|
||||
} // setMajor
|
||||
|
||||
void BLEBeacon::setManufacturerId(uint16_t manufacturerId) {
|
||||
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
|
||||
} // setManufacturerId
|
||||
|
||||
void BLEBeacon::setMinor(uint16_t minor) {
|
||||
m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
|
||||
} // setMinior
|
||||
|
||||
void BLEBeacon::setProximityUUID(BLEUUID uuid) {
|
||||
uuid = uuid.to128();
|
||||
memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16);
|
||||
} // setProximityUUID
|
||||
|
||||
void BLEBeacon::setSignalPower(int8_t signalPower) {
|
||||
m_beaconData.signalPower = signalPower;
|
||||
} // setSignalPower
|
||||
|
||||
|
||||
#endif
|
43
libraries/BLE/src/BLEBeacon.h
Normal file
43
libraries/BLE/src/BLEBeacon.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* BLEBeacon2.h
|
||||
*
|
||||
* Created on: Jan 4, 2018
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEBEACON_H_
|
||||
#include "BLEUUID.h"
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://en.wikipedia.org/wiki/IBeacon
|
||||
*/
|
||||
class BLEBeacon {
|
||||
private:
|
||||
struct {
|
||||
uint16_t manufacturerId;
|
||||
uint8_t subType;
|
||||
uint8_t subTypeLength;
|
||||
uint8_t proximityUUID[16];
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
int8_t signalPower;
|
||||
} __attribute__((packed)) m_beaconData;
|
||||
public:
|
||||
BLEBeacon();
|
||||
std::string getData();
|
||||
uint16_t getMajor();
|
||||
uint16_t getMinor();
|
||||
uint16_t getManufacturerId();
|
||||
BLEUUID getProximityUUID();
|
||||
int8_t getSignalPower();
|
||||
void setData(std::string data);
|
||||
void setMajor(uint16_t major);
|
||||
void setMinor(uint16_t minor);
|
||||
void setManufacturerId(uint16_t manufacturerId);
|
||||
void setProximityUUID(BLEUUID uuid);
|
||||
void setSignalPower(int8_t signalPower);
|
||||
}; // BLEBeacon
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */
|
760
libraries/BLE/src/BLECharacteristic.cpp
Normal file
760
libraries/BLE/src/BLECharacteristic.cpp
Normal file
@ -0,0 +1,760 @@
|
||||
/*
|
||||
* BLECharacteristic.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLE2902.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLECharacteristic";
|
||||
#endif
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID (const char*) for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
*/
|
||||
BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
*/
|
||||
BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
|
||||
m_bleUUID = uuid;
|
||||
m_handle = NULL_HANDLE;
|
||||
m_properties = (esp_gatt_char_prop_t)0;
|
||||
m_pCallbacks = nullptr;
|
||||
|
||||
setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0);
|
||||
setReadProperty((properties & PROPERTY_READ) != 0);
|
||||
setWriteProperty((properties & PROPERTY_WRITE) != 0);
|
||||
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0);
|
||||
setIndicateProperty((properties & PROPERTY_INDICATE) != 0);
|
||||
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0);
|
||||
} // BLECharacteristic
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
BLECharacteristic::~BLECharacteristic() {
|
||||
//free(m_value.attr_value); // Release the storage for the value.
|
||||
} // ~BLECharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Associate a descriptor with this characteristic.
|
||||
* @param [in] pDescriptor
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
|
||||
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
|
||||
ESP_LOGD(LOG_TAG, "<< addDescriptor()");
|
||||
} // addDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register a new characteristic with the ESP runtime.
|
||||
* @param [in] pService The service with which to associate this characteristic.
|
||||
*/
|
||||
void BLECharacteristic::executeCreate(BLEService* pService) {
|
||||
ESP_LOGD(LOG_TAG, ">> executeCreate()");
|
||||
|
||||
if (m_handle != NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "Characteristic already has a handle.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pService = pService; // Save the service to which this characteristic belongs.
|
||||
|
||||
ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s",
|
||||
getUUID().toString().c_str(),
|
||||
m_pService->toString().c_str());
|
||||
|
||||
esp_attr_control_t control;
|
||||
control.auto_rsp = ESP_GATT_RSP_BY_APP;
|
||||
|
||||
m_semaphoreCreateEvt.take("executeCreate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_add_char(
|
||||
m_pService->getHandle(),
|
||||
getUUID().getNative(),
|
||||
static_cast<esp_gatt_perm_t>(m_permissions),
|
||||
getProperties(),
|
||||
nullptr,
|
||||
&control); // Whether to auto respond or not.
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
m_semaphoreCreateEvt.wait("executeCreate");
|
||||
|
||||
BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
|
||||
while (pDescriptor != nullptr) {
|
||||
pDescriptor->executeCreate(this);
|
||||
pDescriptor = m_descriptorMap.getNext();
|
||||
} // End while
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< executeCreate");
|
||||
} // executeCreate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
||||
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
||||
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) {
|
||||
return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID));
|
||||
} // getDescriptorByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
||||
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
||||
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) {
|
||||
return m_descriptorMap.getByUUID(descriptorUUID);
|
||||
} // getDescriptorByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the handle of the characteristic.
|
||||
* @return The handle of the characteristic.
|
||||
*/
|
||||
uint16_t BLECharacteristic::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) {
|
||||
m_permissions = perm;
|
||||
}
|
||||
|
||||
esp_gatt_char_prop_t BLECharacteristic::getProperties() {
|
||||
return m_properties;
|
||||
} // getProperties
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service associated with this characteristic.
|
||||
*/
|
||||
BLEService* BLECharacteristic::getService() {
|
||||
return m_pService;
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the characteristic.
|
||||
* @return The UUID of the characteristic.
|
||||
*/
|
||||
BLEUUID BLECharacteristic::getUUID() {
|
||||
return m_bleUUID;
|
||||
} // getUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current value of the characteristic.
|
||||
* @return A pointer to storage containing the current characteristic value.
|
||||
*/
|
||||
std::string BLECharacteristic::getValue() {
|
||||
return m_value.getValue();
|
||||
} // getValue
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current raw data of the characteristic.
|
||||
* @return A pointer to storage containing the current characteristic data.
|
||||
*/
|
||||
uint8_t* BLECharacteristic::getData() {
|
||||
return m_value.getData();
|
||||
} // getData
|
||||
|
||||
|
||||
/**
|
||||
* Handle a GATT server event.
|
||||
*/
|
||||
void BLECharacteristic::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str());
|
||||
|
||||
switch(event) {
|
||||
// Events handled:
|
||||
//
|
||||
// ESP_GATTS_ADD_CHAR_EVT
|
||||
// ESP_GATTS_CONF_EVT
|
||||
// ESP_GATTS_CONNECT_EVT
|
||||
// ESP_GATTS_DISCONNECT_EVT
|
||||
// ESP_GATTS_EXEC_WRITE_EVT
|
||||
// ESP_GATTS_READ_EVT
|
||||
// ESP_GATTS_WRITE_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTS_EXEC_WRITE_EVT
|
||||
// When we receive this event it is an indication that a previous write long needs to be committed.
|
||||
//
|
||||
// exec_write:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL
|
||||
//
|
||||
case ESP_GATTS_EXEC_WRITE_EVT: {
|
||||
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
|
||||
m_value.commit();
|
||||
if (m_pCallbacks != nullptr) {
|
||||
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} else {
|
||||
m_value.cancel();
|
||||
}
|
||||
// ???
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if,
|
||||
param->write.conn_id,
|
||||
param->write.trans_id, ESP_GATT_OK, nullptr);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_EXEC_WRITE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
|
||||
// add_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
if (getHandle() == param->add_char.attr_handle) {
|
||||
// we have created characteristic, now we can create descriptors
|
||||
// BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
|
||||
// while (pDescriptor != nullptr) {
|
||||
// pDescriptor->executeCreate(this);
|
||||
// pDescriptor = m_descriptorMap.getNext();
|
||||
// } // End while
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
|
||||
//
|
||||
// write:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool need_rsp
|
||||
// - bool is_prep
|
||||
// - uint16_t len
|
||||
// - uint8_t *value
|
||||
//
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
// We check if this write request is for us by comparing the handles in the event. If it is for us
|
||||
// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need
|
||||
// to send a response. If we do, then we formulate a response and send it.
|
||||
if (param->write.handle == m_handle) {
|
||||
if (param->write.is_prep) {
|
||||
m_value.addPart(param->write.value, param->write.len);
|
||||
} else {
|
||||
setValue(param->write.value, param->write.len);
|
||||
}
|
||||
|
||||
ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s",
|
||||
getHandle(), getUUID().toString().c_str());
|
||||
|
||||
char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len);
|
||||
ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData);
|
||||
free(pHexData);
|
||||
|
||||
if (param->write.need_rsp) {
|
||||
esp_gatt_rsp_t rsp;
|
||||
|
||||
rsp.attr_value.len = param->write.len;
|
||||
rsp.attr_value.handle = m_handle;
|
||||
rsp.attr_value.offset = param->write.offset;
|
||||
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
memcpy(rsp.attr_value.value, param->write.value, param->write.len);
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if,
|
||||
param->write.conn_id,
|
||||
param->write.trans_id, ESP_GATT_OK, &rsp);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
} // Response needed
|
||||
|
||||
if (m_pCallbacks != nullptr && param->write.is_prep != true) {
|
||||
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} // Match on handles.
|
||||
break;
|
||||
} // ESP_GATTS_WRITE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
|
||||
//
|
||||
// read:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool is_long
|
||||
// - bool need_rsp
|
||||
//
|
||||
case ESP_GATTS_READ_EVT: {
|
||||
if (param->read.handle == m_handle) {
|
||||
|
||||
|
||||
|
||||
// Here's an interesting thing. The read request has the option of saying whether we need a response
|
||||
// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like
|
||||
// a very strange read.
|
||||
//
|
||||
// We have to handle the case where the data we wish to send back to the client is greater than the maximum
|
||||
// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes.
|
||||
// The apparent algorithm is as follows:
|
||||
//
|
||||
// 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.
|
||||
// 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
|
||||
// 22 bytes, then we "just" send it and thats the end of the story.
|
||||
// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request.
|
||||
// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request.
|
||||
// Because of follow on request processing, we need to maintain an offset of how much data we have already sent
|
||||
// so that when a follow on request arrives, we know where to start in the data to send the next sequence.
|
||||
// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response.
|
||||
// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length.
|
||||
//
|
||||
// The following code has deliberately not been factored to make it fewer statements because this would cloud the
|
||||
// the logic flow comprehension.
|
||||
//
|
||||
|
||||
// get mtu for peer device that we are sending read request to
|
||||
uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1;
|
||||
ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset);
|
||||
if (param->read.need_rsp) {
|
||||
ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)");
|
||||
esp_gatt_rsp_t rsp;
|
||||
|
||||
if (param->read.is_long) {
|
||||
std::string value = m_value.getValue();
|
||||
|
||||
if (value.length() - m_value.getReadOffset() < maxOffset) {
|
||||
// This is the last in the chain
|
||||
rsp.attr_value.len = value.length() - m_value.getReadOffset();
|
||||
rsp.attr_value.offset = m_value.getReadOffset();
|
||||
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
|
||||
m_value.setReadOffset(0);
|
||||
} else {
|
||||
// There will be more to come.
|
||||
rsp.attr_value.len = maxOffset;
|
||||
rsp.attr_value.offset = m_value.getReadOffset();
|
||||
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
|
||||
m_value.setReadOffset(rsp.attr_value.offset + maxOffset);
|
||||
}
|
||||
} else { // read.is_long == false
|
||||
|
||||
std::string value = m_value.getValue();
|
||||
|
||||
if (value.length() + 1 > maxOffset) {
|
||||
// Too big for a single shot entry.
|
||||
m_value.setReadOffset(maxOffset);
|
||||
rsp.attr_value.len = maxOffset;
|
||||
rsp.attr_value.offset = 0;
|
||||
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
|
||||
} else {
|
||||
// Will fit in a single packet with no callbacks required.
|
||||
rsp.attr_value.len = value.length();
|
||||
rsp.attr_value.offset = 0;
|
||||
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
|
||||
}
|
||||
|
||||
if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback
|
||||
m_pCallbacks->onRead(this); // Invoke the read callback.
|
||||
}
|
||||
}
|
||||
rsp.attr_value.handle = param->read.handle;
|
||||
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
|
||||
char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len);
|
||||
ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset);
|
||||
free(pHexData);
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if, param->read.conn_id,
|
||||
param->read.trans_id,
|
||||
ESP_GATT_OK,
|
||||
&rsp);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
} // Response needed
|
||||
} // Handle matches this characteristic.
|
||||
break;
|
||||
} // ESP_GATTS_READ_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_CONF_EVT
|
||||
//
|
||||
// conf:
|
||||
// - esp_gatt_status_t status – The status code.
|
||||
// - uint16_t conn_id – The connection used.
|
||||
//
|
||||
case ESP_GATTS_CONF_EVT: {
|
||||
// ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle);
|
||||
if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet
|
||||
m_semaphoreConfEvt.give(param->conf.status);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTS_DISCONNECT_EVT: {
|
||||
m_semaphoreConfEvt.give();
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
} // default
|
||||
|
||||
} // switch event
|
||||
|
||||
// Give each of the descriptors associated with this characteristic the opportunity to handle the
|
||||
// event.
|
||||
|
||||
m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
|
||||
ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send an indication.
|
||||
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
|
||||
* will block waiting a positive confirmation from the client.
|
||||
* @return N/A
|
||||
*/
|
||||
void BLECharacteristic::indicate() {
|
||||
|
||||
ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length());
|
||||
notify(false);
|
||||
ESP_LOGD(LOG_TAG, "<< indicate");
|
||||
} // indicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a notify.
|
||||
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
|
||||
* will not block; it is a fire and forget.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::notify(bool is_notification) {
|
||||
ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length());
|
||||
|
||||
assert(getService() != nullptr);
|
||||
assert(getService()->getServer() != nullptr);
|
||||
|
||||
GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
|
||||
|
||||
if (getService()->getServer()->getConnectedCount() == 0) {
|
||||
ESP_LOGD(LOG_TAG, "<< notify: No connected clients.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled
|
||||
// and, if not, prevent the notification.
|
||||
|
||||
BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
|
||||
if(is_notification) {
|
||||
if (p2902 != nullptr && !p2902->getNotifications()) {
|
||||
ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (p2902 != nullptr && !p2902->getIndications()) {
|
||||
ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &myPair : getService()->getServer()->getPeerDevices(false)) {
|
||||
uint16_t _mtu = (myPair.second.mtu);
|
||||
if (m_value.getValue().length() > _mtu - 3) {
|
||||
ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
|
||||
}
|
||||
|
||||
size_t length = m_value.getValue().length();
|
||||
if(!is_notification)
|
||||
m_semaphoreConfEvt.take("indicate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_indicate(
|
||||
getService()->getServer()->getGattsIf(),
|
||||
myPair.first,
|
||||
getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc));
|
||||
m_semaphoreConfEvt.give();
|
||||
return;
|
||||
}
|
||||
if(!is_notification)
|
||||
m_semaphoreConfEvt.wait("indicate");
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< notify");
|
||||
} // Notify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the permission to broadcast.
|
||||
* A characteristics has properties associated with it which define what it is capable of doing.
|
||||
* One of these is the broadcast flag.
|
||||
* @param [in] value The flag value of the property.
|
||||
* @return N/A
|
||||
*/
|
||||
void BLECharacteristic::setBroadcastProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
}
|
||||
} // setBroadcastProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callback handlers for this characteristic.
|
||||
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
|
||||
*/
|
||||
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) {
|
||||
ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
|
||||
m_pCallbacks = pCallbacks;
|
||||
ESP_LOGD(LOG_TAG, "<< setCallbacks");
|
||||
} // setCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the BLE handle associated with this characteristic.
|
||||
* A user program will request that a characteristic be created against a service. When the characteristic has been
|
||||
* registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
|
||||
* server/service but it is told to the service, not the characteristic associated with the service. This internally
|
||||
* exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
|
||||
* to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
|
||||
* that will be propagated down to it which contain a handle value and now know that the event is destined for it.
|
||||
* @param [in] handle The handle associated with this characteristic.
|
||||
*/
|
||||
void BLECharacteristic::setHandle(uint16_t handle) {
|
||||
ESP_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
|
||||
m_handle = handle;
|
||||
ESP_LOGD(LOG_TAG, "<< setHandle");
|
||||
} // setHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Indicate property value.
|
||||
* @param [in] value Set to true if we are to allow indicate messages.
|
||||
*/
|
||||
void BLECharacteristic::setIndicateProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
}
|
||||
} // setIndicateProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Notify property value.
|
||||
* @param [in] value Set to true if we are to allow notification messages.
|
||||
*/
|
||||
void BLECharacteristic::setNotifyProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
}
|
||||
} // setNotifyProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Read property value.
|
||||
* @param [in] value Set to true if we are to allow reads.
|
||||
*/
|
||||
void BLECharacteristic::setReadProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
}
|
||||
} // setReadProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic.
|
||||
* @param [in] data The data to set for the characteristic.
|
||||
* @param [in] length The length of the data in bytes.
|
||||
*/
|
||||
void BLECharacteristic::setValue(uint8_t* data, size_t length) {
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, data, length);
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
|
||||
free(pHex);
|
||||
if (length > ESP_GATT_MAX_ATTR_LEN) {
|
||||
ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
|
||||
return;
|
||||
}
|
||||
m_value.setValue(data, length);
|
||||
ESP_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic from string data.
|
||||
* We set the value of the characteristic from the bytes contained in the
|
||||
* string.
|
||||
* @param [in] Set the value of the characteristic.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::setValue(std::string value) {
|
||||
setValue((uint8_t*)(value.data()), value.length());
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(uint16_t& data16) {
|
||||
uint8_t temp[2];
|
||||
temp[0] = data16;
|
||||
temp[1] = data16 >> 8;
|
||||
setValue(temp, 2);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(uint32_t& data32) {
|
||||
uint8_t temp[4];
|
||||
temp[0] = data32;
|
||||
temp[1] = data32 >> 8;
|
||||
temp[2] = data32 >> 16;
|
||||
temp[3] = data32 >> 24;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(int& data32) {
|
||||
uint8_t temp[4];
|
||||
temp[0] = data32;
|
||||
temp[1] = data32 >> 8;
|
||||
temp[2] = data32 >> 16;
|
||||
temp[3] = data32 >> 24;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(float& data32) {
|
||||
uint8_t temp[4];
|
||||
*((float*)temp) = data32;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(double& data64) {
|
||||
uint8_t temp[8];
|
||||
*((double*)temp) = data64;
|
||||
setValue(temp, 8);
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Write No Response property value.
|
||||
* @param [in] value Set to true if we are to allow writes with no response.
|
||||
*/
|
||||
void BLECharacteristic::setWriteNoResponseProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
}
|
||||
} // setWriteNoResponseProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Write property value.
|
||||
* @param [in] value Set to true if we are to allow writes.
|
||||
*/
|
||||
void BLECharacteristic::setWriteProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
}
|
||||
} // setWriteProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the characteristic.
|
||||
* @return A string representation of the characteristic.
|
||||
*/
|
||||
std::string BLECharacteristic::toString() {
|
||||
std::stringstream stringstream;
|
||||
stringstream << std::hex << std::setfill('0');
|
||||
stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
|
||||
stringstream << " " <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_READ) ? "Read " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) ? "Write " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) ? "WriteNoResponse " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "Broadcast " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) ? "Notify " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) ? "Indicate " : "");
|
||||
return stringstream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
*/
|
||||
void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default");
|
||||
ESP_LOGD("BLECharacteristicCallbacks", "<< onRead");
|
||||
} // onRead
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
*/
|
||||
void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default");
|
||||
ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite");
|
||||
} // onWrite
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
137
libraries/BLE/src/BLECharacteristic.h
Normal file
137
libraries/BLE/src/BLECharacteristic.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* BLECharacteristic.h
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "BLEUUID.h"
|
||||
#include <esp_gatts_api.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include "BLEDescriptor.h"
|
||||
#include "BLEValue.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEService;
|
||||
class BLEDescriptor;
|
||||
class BLECharacteristicCallbacks;
|
||||
|
||||
/**
|
||||
* @brief A management structure for %BLE descriptors.
|
||||
*/
|
||||
class BLEDescriptorMap {
|
||||
public:
|
||||
void setByUUID(const char* uuid, BLEDescriptor* pDescriptor);
|
||||
void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor);
|
||||
void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor);
|
||||
BLEDescriptor* getByUUID(const char* uuid);
|
||||
BLEDescriptor* getByUUID(BLEUUID uuid);
|
||||
BLEDescriptor* getByHandle(uint16_t handle);
|
||||
std::string toString();
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
|
||||
BLEDescriptor* getFirst();
|
||||
BLEDescriptor* getNext();
|
||||
private:
|
||||
std::map<BLEDescriptor*, std::string> m_uuidMap;
|
||||
std::map<uint16_t, BLEDescriptor*> m_handleMap;
|
||||
std::map<BLEDescriptor*, std::string>::iterator m_iterator;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE Characteristic.
|
||||
*
|
||||
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and
|
||||
* can be read and written to by a %BLE client.
|
||||
*/
|
||||
class BLECharacteristic {
|
||||
public:
|
||||
BLECharacteristic(const char* uuid, uint32_t properties = 0);
|
||||
BLECharacteristic(BLEUUID uuid, uint32_t properties = 0);
|
||||
virtual ~BLECharacteristic();
|
||||
|
||||
void addDescriptor(BLEDescriptor* pDescriptor);
|
||||
BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID);
|
||||
BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID);
|
||||
BLEUUID getUUID();
|
||||
std::string getValue();
|
||||
uint8_t* getData();
|
||||
|
||||
void indicate();
|
||||
void notify(bool is_notification = true);
|
||||
void setBroadcastProperty(bool value);
|
||||
void setCallbacks(BLECharacteristicCallbacks* pCallbacks);
|
||||
void setIndicateProperty(bool value);
|
||||
void setNotifyProperty(bool value);
|
||||
void setReadProperty(bool value);
|
||||
void setValue(uint8_t* data, size_t size);
|
||||
void setValue(std::string value);
|
||||
void setValue(uint16_t& data16);
|
||||
void setValue(uint32_t& data32);
|
||||
void setValue(int& data32);
|
||||
void setValue(float& data32);
|
||||
void setValue(double& data64);
|
||||
void setWriteProperty(bool value);
|
||||
void setWriteNoResponseProperty(bool value);
|
||||
std::string toString();
|
||||
uint16_t getHandle();
|
||||
void setAccessPermissions(esp_gatt_perm_t perm);
|
||||
|
||||
static const uint32_t PROPERTY_READ = 1<<0;
|
||||
static const uint32_t PROPERTY_WRITE = 1<<1;
|
||||
static const uint32_t PROPERTY_NOTIFY = 1<<2;
|
||||
static const uint32_t PROPERTY_BROADCAST = 1<<3;
|
||||
static const uint32_t PROPERTY_INDICATE = 1<<4;
|
||||
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
|
||||
|
||||
private:
|
||||
|
||||
friend class BLEServer;
|
||||
friend class BLEService;
|
||||
friend class BLEDescriptor;
|
||||
friend class BLECharacteristicMap;
|
||||
|
||||
BLEUUID m_bleUUID;
|
||||
BLEDescriptorMap m_descriptorMap;
|
||||
uint16_t m_handle;
|
||||
esp_gatt_char_prop_t m_properties;
|
||||
BLECharacteristicCallbacks* m_pCallbacks;
|
||||
BLEService* m_pService;
|
||||
BLEValue m_value;
|
||||
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||
|
||||
void handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
void executeCreate(BLEService* pService);
|
||||
esp_gatt_char_prop_t getProperties();
|
||||
BLEService* getService();
|
||||
void setHandle(uint16_t handle);
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
|
||||
}; // BLECharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
|
||||
*
|
||||
* When a server application creates a %BLE characteristic, we may wish to be informed when there is either
|
||||
* a read or write request to the characteristic's value. An application can register a
|
||||
* sub-classed instance of this class and will be notified when such an event happens.
|
||||
*/
|
||||
class BLECharacteristicCallbacks {
|
||||
public:
|
||||
virtual ~BLECharacteristicCallbacks();
|
||||
virtual void onRead(BLECharacteristic* pCharacteristic);
|
||||
virtual void onWrite(BLECharacteristic* pCharacteristic);
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */
|
133
libraries/BLE/src/BLECharacteristicMap.cpp
Normal file
133
libraries/BLE/src/BLECharacteristicMap.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* BLECharacteristicMap.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "BLEService.h"
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by handle.
|
||||
* @param [in] handle The handle to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) {
|
||||
return m_handleMap.at(handle);
|
||||
} // getByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by UUID.
|
||||
* @param [in] UUID The UUID to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) {
|
||||
return getByUUID(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by UUID.
|
||||
* @param [in] UUID The UUID to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) {
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (myPair.first->getUUID().equals(uuid)) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
//return m_uuidMap.at(uuid.toString());
|
||||
return nullptr;
|
||||
} // getByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the first characteristic in the map.
|
||||
* @return The first characteristic in the map.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getFirst() {
|
||||
m_iterator = m_uuidMap.begin();
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLECharacteristic* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getFirst
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the next characteristic in the map.
|
||||
* @return The next characteristic in the map.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getNext() {
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLECharacteristic* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getNext
|
||||
|
||||
|
||||
/**
|
||||
* @brief Pass the GATT server event onwards to each of the characteristics found in the mapping
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
|
||||
// Invoke the handler for every Service we have.
|
||||
for (auto& myPair : m_uuidMap) {
|
||||
myPair.first->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the characteristic by handle.
|
||||
* @param [in] handle The handle of the characteristic.
|
||||
* @param [in] characteristic The characteristic to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) {
|
||||
m_handleMap.insert(std::pair<uint16_t, BLECharacteristic*>(handle, characteristic));
|
||||
} // setByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the characteristic by UUID.
|
||||
* @param [in] uuid The uuid of the characteristic.
|
||||
* @param [in] characteristic The characteristic to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) {
|
||||
m_uuidMap.insert(std::pair<BLECharacteristic*, std::string>(pCharacteristic, uuid.toString()));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the characteristic map.
|
||||
* @return A string representation of the characteristic map.
|
||||
*/
|
||||
std::string BLECharacteristicMap::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << std::hex << std::setfill('0');
|
||||
int count = 0;
|
||||
for (auto &myPair: m_uuidMap) {
|
||||
if (count > 0) {
|
||||
stringStream << "\n";
|
||||
}
|
||||
count++;
|
||||
stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString();
|
||||
}
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
536
libraries/BLE/src/BLEClient.cpp
Normal file
536
libraries/BLE/src/BLEClient.cpp
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* BLEDevice.cpp
|
||||
*
|
||||
* Created on: Mar 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_bt.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gattc_api.h>
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEService.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include "BLEDevice.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEClient";
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Design
|
||||
* ------
|
||||
* When we perform a searchService() requests, we are asking the BLE server to return each of the services
|
||||
* that it exposes. For each service, we received an ESP_GATTC_SEARCH_RES_EVT event which contains details
|
||||
* of the exposed service including its UUID.
|
||||
*
|
||||
* The objects we will invent for a BLEClient will be as follows:
|
||||
* * BLERemoteService - A model of a remote service.
|
||||
* * BLERemoteCharacteristic - A model of a remote characteristic
|
||||
* * BLERemoteDescriptor - A model of a remote descriptor.
|
||||
*
|
||||
* Since there is a hierarchical relationship here, we will have the idea that from a BLERemoteService will own
|
||||
* zero or more remote characteristics and a BLERemoteCharacteristic will own zero or more remote BLEDescriptors.
|
||||
*
|
||||
* We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics
|
||||
* and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
BLEClient::BLEClient() {
|
||||
m_pClientCallbacks = nullptr;
|
||||
m_conn_id = ESP_GATT_IF_NONE;
|
||||
m_gattc_if = ESP_GATT_IF_NONE;
|
||||
m_haveServices = false;
|
||||
m_isConnected = false; // Initially, we are flagged as not connected.
|
||||
} // BLEClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
BLEClient::~BLEClient() {
|
||||
// We may have allocated service references associated with this client. Before we are finished
|
||||
// with the client, we must release resources.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
delete myPair.second;
|
||||
}
|
||||
m_servicesMap.clear();
|
||||
} // ~BLEClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Clear any existing services.
|
||||
*
|
||||
*/
|
||||
void BLEClient::clearServices() {
|
||||
ESP_LOGD(LOG_TAG, ">> clearServices");
|
||||
// Delete all the services.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
delete myPair.second;
|
||||
}
|
||||
m_servicesMap.clear();
|
||||
m_haveServices = false;
|
||||
ESP_LOGD(LOG_TAG, "<< clearServices");
|
||||
} // clearServices
|
||||
|
||||
/**
|
||||
* Add overloaded function to ease connect to peer device with not public address
|
||||
*/
|
||||
bool BLEClient::connect(BLEAdvertisedDevice* device) {
|
||||
BLEAddress address = device->getAddress();
|
||||
esp_ble_addr_type_t type = device->getAddressType();
|
||||
return connect(address, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to the partner (BLE Server).
|
||||
* @param [in] address The address of the partner.
|
||||
* @return True on success.
|
||||
*/
|
||||
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
|
||||
ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
|
||||
|
||||
// We need the connection handle that we get from registering the application. We register the app
|
||||
// and then block on its completion. When the event has arrived, we will have the handle.
|
||||
m_appId = BLEDevice::m_appId++;
|
||||
BLEDevice::addPeerDevice(this, true, m_appId);
|
||||
m_semaphoreRegEvt.take("connect");
|
||||
|
||||
// clearServices(); // we dont need to delete services since every client is unique?
|
||||
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_semaphoreRegEvt.wait("connect");
|
||||
|
||||
m_peerAddress = address;
|
||||
|
||||
// Perform the open connection request against the target BLE Server.
|
||||
m_semaphoreOpenEvt.take("connect");
|
||||
errRc = ::esp_ble_gattc_open(
|
||||
m_gattc_if,
|
||||
*getPeerAddress().getNative(), // address
|
||||
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
|
||||
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
|
||||
);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
|
||||
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
|
||||
return rc == ESP_GATT_OK;
|
||||
} // connect
|
||||
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the peer.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEClient::disconnect() {
|
||||
ESP_LOGD(LOG_TAG, ">> disconnect()");
|
||||
esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< disconnect()");
|
||||
} // disconnect
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT Client events
|
||||
*/
|
||||
void BLEClient::gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
|
||||
|
||||
// Execute handler code based on the type of event received.
|
||||
switch(event) {
|
||||
|
||||
case ESP_GATTC_SRVC_CHG_EVT:
|
||||
ESP_LOGI(LOG_TAG, "SERVICE CHANGED");
|
||||
break;
|
||||
|
||||
case ESP_GATTC_CLOSE_EVT: {
|
||||
// esp_ble_gattc_app_unregister(m_appId);
|
||||
// BLEDevice::removePeerDevice(m_gattc_if, true);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// ESP_GATTC_DISCONNECT_EVT
|
||||
//
|
||||
// disconnect:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
// If we receive a disconnect event, set the class flag that indicates that we are
|
||||
// no longer connected.
|
||||
m_isConnected = false;
|
||||
if (m_pClientCallbacks != nullptr) {
|
||||
m_pClientCallbacks->onDisconnect(this);
|
||||
}
|
||||
BLEDevice::removePeerDevice(m_appId, true);
|
||||
esp_ble_gattc_app_unregister(m_gattc_if);
|
||||
m_semaphoreRssiCmplEvt.give();
|
||||
m_semaphoreSearchCmplEvt.give(1);
|
||||
break;
|
||||
} // ESP_GATTC_DISCONNECT_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTC_OPEN_EVT
|
||||
//
|
||||
// open:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
//
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
m_conn_id = evtParam->open.conn_id;
|
||||
if (m_pClientCallbacks != nullptr) {
|
||||
m_pClientCallbacks->onConnect(this);
|
||||
}
|
||||
if (evtParam->open.status == ESP_GATT_OK) {
|
||||
m_isConnected = true; // Flag us as connected.
|
||||
}
|
||||
m_semaphoreOpenEvt.give(evtParam->open.status);
|
||||
break;
|
||||
} // ESP_GATTC_OPEN_EVT
|
||||
|
||||
|
||||
//
|
||||
// ESP_GATTC_REG_EVT
|
||||
//
|
||||
// reg:
|
||||
// esp_gatt_status_t status
|
||||
// uint16_t app_id
|
||||
//
|
||||
case ESP_GATTC_REG_EVT: {
|
||||
m_gattc_if = gattc_if;
|
||||
m_semaphoreRegEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_REG_EVT
|
||||
|
||||
case ESP_GATTC_CFG_MTU_EVT:
|
||||
if(evtParam->cfg_mtu.status != ESP_GATT_OK) {
|
||||
ESP_LOGE(LOG_TAG,"Config mtu failed");
|
||||
}
|
||||
m_mtu = evtParam->cfg_mtu.mtu;
|
||||
break;
|
||||
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
BLEDevice::updatePeerDevice(this, true, m_gattc_if);
|
||||
esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTC_CONNECT_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTC_SEARCH_CMPL_EVT
|
||||
//
|
||||
// search_cmpl:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
//
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam;
|
||||
if (p_data->search_cmpl.status != ESP_GATT_OK){
|
||||
ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
|
||||
break;
|
||||
}
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
// commented out just for now to keep backward compatibility
|
||||
// if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) {
|
||||
// ESP_LOGI(LOG_TAG, "Get service information from remote device");
|
||||
// } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) {
|
||||
// ESP_LOGI(LOG_TAG, "Get service information from flash");
|
||||
// } else {
|
||||
// ESP_LOGI(LOG_TAG, "unknown service source");
|
||||
// }
|
||||
#endif
|
||||
m_semaphoreSearchCmplEvt.give(0);
|
||||
break;
|
||||
} // ESP_GATTC_SEARCH_CMPL_EVT
|
||||
|
||||
|
||||
//
|
||||
// ESP_GATTC_SEARCH_RES_EVT
|
||||
//
|
||||
// search_res:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t start_handle
|
||||
// - uint16_t end_handle
|
||||
// - esp_gatt_id_t srvc_id
|
||||
//
|
||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||
BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id);
|
||||
BLERemoteService* pRemoteService = new BLERemoteService(
|
||||
evtParam->search_res.srvc_id,
|
||||
this,
|
||||
evtParam->search_res.start_handle,
|
||||
evtParam->search_res.end_handle
|
||||
);
|
||||
m_servicesMap.insert(std::pair<std::string, BLERemoteService*>(uuid.toString(), pRemoteService));
|
||||
m_servicesMapByInstID.insert(std::pair<BLERemoteService *, uint16_t>(pRemoteService, evtParam->search_res.srvc_id.inst_id));
|
||||
break;
|
||||
} // ESP_GATTC_SEARCH_RES_EVT
|
||||
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // Switch
|
||||
|
||||
// Pass the request on to all services.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
|
||||
}
|
||||
|
||||
} // gattClientEventHandler
|
||||
|
||||
|
||||
uint16_t BLEClient::getConnId() {
|
||||
return m_conn_id;
|
||||
} // getConnId
|
||||
|
||||
|
||||
|
||||
esp_gatt_if_t BLEClient::getGattcIf() {
|
||||
return m_gattc_if;
|
||||
} // getGattcIf
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the address of the peer.
|
||||
*
|
||||
* Returns the Bluetooth device address of the %BLE peer to which this client is connected.
|
||||
*/
|
||||
BLEAddress BLEClient::getPeerAddress() {
|
||||
return m_peerAddress;
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ask the BLE server for the RSSI value.
|
||||
* @return The RSSI value.
|
||||
*/
|
||||
int BLEClient::getRssi() {
|
||||
ESP_LOGD(LOG_TAG, ">> getRssi()");
|
||||
if (!isConnected()) {
|
||||
ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected");
|
||||
return 0;
|
||||
}
|
||||
// We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive
|
||||
// an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion.
|
||||
//
|
||||
m_semaphoreRssiCmplEvt.take("getRssi");
|
||||
esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative());
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc));
|
||||
return 0;
|
||||
}
|
||||
int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi");
|
||||
ESP_LOGD(LOG_TAG, "<< getRssi(): %d", rssiValue);
|
||||
return rssiValue;
|
||||
} // getRssi
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service BLE Remote Service instance corresponding to the uuid.
|
||||
* @param [in] uuid The UUID of the service being sought.
|
||||
* @return A reference to the Service or nullptr if don't know about it.
|
||||
*/
|
||||
BLERemoteService* BLEClient::getService(const char* uuid) {
|
||||
return getService(BLEUUID(uuid));
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service object corresponding to the uuid.
|
||||
* @param [in] uuid The UUID of the service being sought.
|
||||
* @return A reference to the Service or nullptr if don't know about it.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
|
||||
ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
|
||||
// Design
|
||||
// ------
|
||||
// We wish to retrieve the service given its UUID. It is possible that we have not yet asked the
|
||||
// device what services it has in which case we have nothing to match against. If we have not
|
||||
// asked the device about its services, then we do that now. Once we get the results we can then
|
||||
// examine the services map to see if it has the service we are looking for.
|
||||
if (!m_haveServices) {
|
||||
getServices();
|
||||
}
|
||||
std::string uuidStr = uuid.toString();
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
if (myPair.first == uuidStr) {
|
||||
ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str());
|
||||
return myPair.second;
|
||||
}
|
||||
} // End of each of the services.
|
||||
ESP_LOGD(LOG_TAG, "<< getService: not found");
|
||||
return nullptr;
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ask the remote %BLE server for its services.
|
||||
* A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
|
||||
* services and wait until we have received them all.
|
||||
* @return N/A
|
||||
*/
|
||||
std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
|
||||
/*
|
||||
* Design
|
||||
* ------
|
||||
* We invoke esp_ble_gattc_search_service. This will request a list of the service exposed by the
|
||||
* peer BLE partner to be returned as events. Each event will be an an instance of ESP_GATTC_SEARCH_RES_EVT
|
||||
* and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received.
|
||||
*/
|
||||
ESP_LOGD(LOG_TAG, ">> getServices");
|
||||
// TODO implement retrieving services from cache
|
||||
clearServices(); // Clear any services that may exist.
|
||||
|
||||
esp_err_t errRc = esp_ble_gattc_search_service(
|
||||
getGattcIf(),
|
||||
getConnId(),
|
||||
NULL // Filter UUID
|
||||
);
|
||||
|
||||
m_semaphoreSearchCmplEvt.take("getServices");
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return &m_servicesMap;
|
||||
}
|
||||
// If sucessfull, remember that we now have services.
|
||||
m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0);
|
||||
ESP_LOGD(LOG_TAG, "<< getServices");
|
||||
return &m_servicesMap;
|
||||
} // getServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of a specific characteristic associated with a specific service.
|
||||
* @param [in] serviceUUID The service that owns the characteristic.
|
||||
* @param [in] characteristicUUID The characteristic whose value we wish to read.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) {
|
||||
ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue();
|
||||
ESP_LOGD(LOG_TAG, "<<getValue");
|
||||
return ret;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle a received GAP event.
|
||||
*
|
||||
* @param [in] event
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEClient::handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param) {
|
||||
ESP_LOGD(LOG_TAG, "BLEClient ... handling GAP event!");
|
||||
switch (event) {
|
||||
//
|
||||
// ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
|
||||
//
|
||||
// read_rssi_cmpl
|
||||
// - esp_bt_status_t status
|
||||
// - int8_t rssi
|
||||
// - esp_bd_addr_t remote_addr
|
||||
//
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
|
||||
m_semaphoreRssiCmplEvt.give((uint32_t) param->read_rssi_cmpl.rssi);
|
||||
break;
|
||||
} // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} // handleGAPEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Are we connected to a partner?
|
||||
* @return True if we are connected and false if we are not connected.
|
||||
*/
|
||||
bool BLEClient::isConnected() {
|
||||
return m_isConnected;
|
||||
} // isConnected
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callbacks that will be invoked.
|
||||
*/
|
||||
void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
|
||||
m_pClientCallbacks = pClientCallbacks;
|
||||
} // setClientCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of a specific characteristic associated with a specific service.
|
||||
* @param [in] serviceUUID The service that owns the characteristic.
|
||||
* @param [in] characteristicUUID The characteristic whose value we wish to write.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value);
|
||||
ESP_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
uint16_t BLEClient::getMTU() {
|
||||
return m_mtu;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of this client.
|
||||
* @return A string representation of this client.
|
||||
*/
|
||||
std::string BLEClient::toString() {
|
||||
std::ostringstream ss;
|
||||
ss << "peer address: " << m_peerAddress.toString();
|
||||
ss << "\nServices:\n";
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
ss << myPair.second->toString() << "\n";
|
||||
// myPair.second is the value
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
103
libraries/BLE/src/BLEClient.h
Normal file
103
libraries/BLE/src/BLEClient.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* BLEDevice.h
|
||||
*
|
||||
* Created on: Mar 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef MAIN_BLEDEVICE_H_
|
||||
#define MAIN_BLEDEVICE_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "BLEExceptions.h"
|
||||
#include "BLERemoteService.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEAddress.h"
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
|
||||
class BLERemoteService;
|
||||
class BLEClientCallbacks;
|
||||
class BLEAdvertisedDevice;
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE client.
|
||||
*/
|
||||
class BLEClient {
|
||||
public:
|
||||
BLEClient();
|
||||
~BLEClient();
|
||||
|
||||
bool connect(BLEAdvertisedDevice* device);
|
||||
bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server
|
||||
void disconnect(); // Disconnect from the remote BLE Server
|
||||
BLEAddress getPeerAddress(); // Get the address of the remote BLE Server
|
||||
int getRssi(); // Get the RSSI of the remote BLE Server
|
||||
std::map<std::string, BLERemoteService*>* getServices(); // Get a map of the services offered by the remote BLE Server
|
||||
BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server.
|
||||
BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server.
|
||||
std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service.
|
||||
|
||||
|
||||
void handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
|
||||
bool isConnected(); // Return true if we are connected.
|
||||
|
||||
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
|
||||
void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service.
|
||||
|
||||
std::string toString(); // Return a string representation of this client.
|
||||
uint16_t getConnId();
|
||||
esp_gatt_if_t getGattcIf();
|
||||
uint16_t getMTU();
|
||||
|
||||
uint16_t m_appId;
|
||||
private:
|
||||
friend class BLEDevice;
|
||||
friend class BLERemoteService;
|
||||
friend class BLERemoteCharacteristic;
|
||||
friend class BLERemoteDescriptor;
|
||||
|
||||
void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param);
|
||||
|
||||
BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server.
|
||||
uint16_t m_conn_id;
|
||||
// int m_deviceType;
|
||||
esp_gatt_if_t m_gattc_if;
|
||||
bool m_haveServices = false; // Have we previously obtain the set of services from the remote server.
|
||||
bool m_isConnected = false; // Are we currently connected.
|
||||
|
||||
BLEClientCallbacks* m_pClientCallbacks;
|
||||
FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
|
||||
std::map<std::string, BLERemoteService*> m_servicesMap;
|
||||
std::map<BLERemoteService*, uint16_t> m_servicesMapByInstID;
|
||||
void clearServices(); // Clear any existing services.
|
||||
uint16_t m_mtu = 23;
|
||||
}; // class BLEDevice
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks associated with a %BLE client.
|
||||
*/
|
||||
class BLEClientCallbacks {
|
||||
public:
|
||||
virtual ~BLEClientCallbacks() {};
|
||||
virtual void onConnect(BLEClient *pClient) = 0;
|
||||
virtual void onDisconnect(BLEClient *pClient) = 0;
|
||||
};
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDEVICE_H_ */
|
296
libraries/BLE/src/BLEDescriptor.cpp
Normal file
296
libraries/BLE/src/BLEDescriptor.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* BLEDescriptor.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLEService.h"
|
||||
#include "BLEDescriptor.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEDescriptor";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor constructor.
|
||||
*/
|
||||
BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor constructor.
|
||||
*/
|
||||
BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) {
|
||||
m_bleUUID = uuid;
|
||||
m_value.attr_len = 0; // Initial length is 0.
|
||||
m_value.attr_max_len = max_len; // Maximum length of the data.
|
||||
m_handle = NULL_HANDLE; // Handle is initially unknown.
|
||||
m_pCharacteristic = nullptr; // No initial characteristic.
|
||||
m_pCallback = nullptr; // No initial callback.
|
||||
|
||||
m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value.
|
||||
} // BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor destructor.
|
||||
*/
|
||||
BLEDescriptor::~BLEDescriptor() {
|
||||
free(m_value.attr_value); // Release the storage we created in the constructor.
|
||||
} // ~BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Execute the creation of the descriptor with the BLE runtime in ESP.
|
||||
* @param [in] pCharacteristic The characteristic to which to register this descriptor.
|
||||
*/
|
||||
void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str());
|
||||
|
||||
if (m_handle != NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "Descriptor already has a handle.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service.
|
||||
|
||||
esp_attr_control_t control;
|
||||
control.auto_rsp = ESP_GATT_AUTO_RSP;
|
||||
m_semaphoreCreateEvt.take("executeCreate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_add_char_descr(
|
||||
pCharacteristic->getService()->getHandle(),
|
||||
getUUID().getNative(),
|
||||
(esp_gatt_perm_t)m_permissions,
|
||||
&m_value,
|
||||
&control);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreCreateEvt.wait("executeCreate");
|
||||
ESP_LOGD(LOG_TAG, "<< executeCreate");
|
||||
} // executeCreate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the BLE handle for this descriptor.
|
||||
* @return The handle for this descriptor.
|
||||
*/
|
||||
uint16_t BLEDescriptor::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the length of the value of this descriptor.
|
||||
* @return The length (in bytes) of the value of this descriptor.
|
||||
*/
|
||||
size_t BLEDescriptor::getLength() {
|
||||
return m_value.attr_len;
|
||||
} // getLength
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the descriptor.
|
||||
*/
|
||||
BLEUUID BLEDescriptor::getUUID() {
|
||||
return m_bleUUID;
|
||||
} // getUUID
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of this descriptor.
|
||||
* @return A pointer to the value of this descriptor.
|
||||
*/
|
||||
uint8_t* BLEDescriptor::getValue() {
|
||||
return m_value.attr_value;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT server events for the descripttor.
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEDescriptor::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
switch (event) {
|
||||
// ESP_GATTS_ADD_CHAR_DESCR_EVT
|
||||
//
|
||||
// add_char_descr:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
case ESP_GATTS_ADD_CHAR_DESCR_EVT: {
|
||||
if (m_pCharacteristic != nullptr &&
|
||||
m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) &&
|
||||
m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle &&
|
||||
m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) {
|
||||
setHandle(param->add_char_descr.attr_handle);
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_DESCR_EVT
|
||||
|
||||
// ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived.
|
||||
//
|
||||
// write:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool need_rsp
|
||||
// - bool is_prep
|
||||
// - uint16_t len
|
||||
// - uint8_t *value
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
if (param->write.handle == m_handle) {
|
||||
setValue(param->write.value, param->write.len); // Set the value of the descriptor.
|
||||
|
||||
if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now.
|
||||
m_pCallback->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} // End of ... this is our handle.
|
||||
|
||||
break;
|
||||
} // ESP_GATTS_WRITE_EVT
|
||||
|
||||
// ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived.
|
||||
//
|
||||
// read:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool is_long
|
||||
// - bool need_rsp
|
||||
//
|
||||
case ESP_GATTS_READ_EVT: {
|
||||
if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it
|
||||
|
||||
if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now.
|
||||
m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler.
|
||||
}
|
||||
|
||||
} // End of this is our handle
|
||||
break;
|
||||
} // ESP_GATTS_READ_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch event
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callback handlers for this descriptor.
|
||||
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
|
||||
*/
|
||||
void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) {
|
||||
ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback);
|
||||
m_pCallback = pCallback;
|
||||
ESP_LOGD(LOG_TAG, "<< setCallbacks");
|
||||
} // setCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the handle of this descriptor.
|
||||
* Set the handle of this descriptor to be the supplied value.
|
||||
* @param [in] handle The handle to be associated with this descriptor.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptor::setHandle(uint16_t handle) {
|
||||
ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
|
||||
m_handle = handle;
|
||||
ESP_LOGD(LOG_TAG, "<< setHandle()");
|
||||
} // setHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the descriptor.
|
||||
* @param [in] data The data to set for the descriptor.
|
||||
* @param [in] length The length of the data in bytes.
|
||||
*/
|
||||
void BLEDescriptor::setValue(uint8_t* data, size_t length) {
|
||||
if (length > ESP_GATT_MAX_ATTR_LEN) {
|
||||
ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
|
||||
return;
|
||||
}
|
||||
m_value.attr_len = length;
|
||||
memcpy(m_value.attr_value, data, length);
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the descriptor.
|
||||
* @param [in] value The value of the descriptor in string form.
|
||||
*/
|
||||
void BLEDescriptor::setValue(std::string value) {
|
||||
setValue((uint8_t*) value.data(), value.length());
|
||||
} // setValue
|
||||
|
||||
void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) {
|
||||
m_permissions = perm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the descriptor.
|
||||
* @return A string representation of the descriptor.
|
||||
*/
|
||||
std::string BLEDescriptor::toString() {
|
||||
std::stringstream stringstream;
|
||||
stringstream << std::hex << std::setfill('0');
|
||||
stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
|
||||
return stringstream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {}
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default");
|
||||
ESP_LOGD("BLEDescriptorCallbacks", "<< onRead");
|
||||
} // onRead
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default");
|
||||
ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite");
|
||||
} // onWrite
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
77
libraries/BLE/src/BLEDescriptor.h
Normal file
77
libraries/BLE/src/BLEDescriptor.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* BLEDescriptor.h
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
#include "BLEUUID.h"
|
||||
#include "BLECharacteristic.h"
|
||||
#include <esp_gatts_api.h>
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEService;
|
||||
class BLECharacteristic;
|
||||
class BLEDescriptorCallbacks;
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE descriptor.
|
||||
*/
|
||||
class BLEDescriptor {
|
||||
public:
|
||||
BLEDescriptor(const char* uuid, uint16_t max_len = 100);
|
||||
BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100);
|
||||
virtual ~BLEDescriptor();
|
||||
|
||||
uint16_t getHandle(); // Get the handle of the descriptor.
|
||||
size_t getLength(); // Get the length of the value of the descriptor.
|
||||
BLEUUID getUUID(); // Get the UUID of the descriptor.
|
||||
uint8_t* getValue(); // Get a pointer to the value of the descriptor.
|
||||
void handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor.
|
||||
void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor.
|
||||
void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data.
|
||||
void setValue(std::string value); // Set the value of the descriptor as a data buffer.
|
||||
|
||||
std::string toString(); // Convert the descriptor to a string representation.
|
||||
|
||||
private:
|
||||
friend class BLEDescriptorMap;
|
||||
friend class BLECharacteristic;
|
||||
BLEUUID m_bleUUID;
|
||||
uint16_t m_handle;
|
||||
BLEDescriptorCallbacks* m_pCallback;
|
||||
BLECharacteristic* m_pCharacteristic;
|
||||
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
esp_attr_value_t m_value;
|
||||
|
||||
void executeCreate(BLECharacteristic* pCharacteristic);
|
||||
void setHandle(uint16_t handle);
|
||||
}; // BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
|
||||
*
|
||||
* When a server application creates a %BLE descriptor, we may wish to be informed when there is either
|
||||
* a read or write request to the descriptors value. An application can register a
|
||||
* sub-classed instance of this class and will be notified when such an event happens.
|
||||
*/
|
||||
class BLEDescriptorCallbacks {
|
||||
public:
|
||||
virtual ~BLEDescriptorCallbacks();
|
||||
virtual void onRead(BLEDescriptor* pDescriptor);
|
||||
virtual void onWrite(BLEDescriptor* pDescriptor);
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */
|
147
libraries/BLE/src/BLEDescriptorMap.cpp
Normal file
147
libraries/BLE/src/BLEDescriptorMap.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* BLEDescriptorMap.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEDescriptor.h"
|
||||
#include <esp_gatts_api.h> // ESP32 BLE
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by UUID.
|
||||
* @param [in] UUID The UUID to look up the descriptor.
|
||||
* @return The descriptor. If not present, then nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) {
|
||||
return getByUUID(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by UUID.
|
||||
* @param [in] UUID The UUID to look up the descriptor.
|
||||
* @return The descriptor. If not present, then nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) {
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (myPair.first->getUUID().equals(uuid)) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
//return m_uuidMap.at(uuid.toString());
|
||||
return nullptr;
|
||||
} // getByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by handle.
|
||||
* @param [in] handle The handle to look up the descriptor.
|
||||
* @return The descriptor.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) {
|
||||
return m_handleMap.at(handle);
|
||||
} // getByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by UUID.
|
||||
* @param [in] uuid The uuid of the descriptor.
|
||||
* @param [in] characteristic The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){
|
||||
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by UUID.
|
||||
* @param [in] uuid The uuid of the descriptor.
|
||||
* @param [in] characteristic The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) {
|
||||
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid.toString()));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by handle.
|
||||
* @param [in] handle The handle of the descriptor.
|
||||
* @param [in] descriptor The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) {
|
||||
m_handleMap.insert(std::pair<uint16_t, BLEDescriptor*>(handle, pDescriptor));
|
||||
} // setByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the descriptor map.
|
||||
* @return A string representation of the descriptor map.
|
||||
*/
|
||||
std::string BLEDescriptorMap::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << std::hex << std::setfill('0');
|
||||
int count = 0;
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (count > 0) {
|
||||
stringStream << "\n";
|
||||
}
|
||||
count++;
|
||||
stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString();
|
||||
}
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @breif Pass the GATT server event onwards to each of the descriptors found in the mapping
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEDescriptorMap::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
// Invoke the handler for every descriptor we have.
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
myPair.first->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the first descriptor in the map.
|
||||
* @return The first descriptor in the map.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getFirst() {
|
||||
m_iterator = m_uuidMap.begin();
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEDescriptor* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getFirst
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the next descriptor in the map.
|
||||
* @return The next descriptor in the map.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getNext() {
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEDescriptor* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getNext
|
||||
#endif /* CONFIG_BT_ENABLED */
|
650
libraries/BLE/src/BLEDevice.cpp
Normal file
650
libraries/BLE/src/BLEDevice.cpp
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* BLE.cpp
|
||||
*
|
||||
* Created on: Mar 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_err.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_bt.h> // ESP32 BLE
|
||||
#include <esp_bt_device.h> // ESP32 BLE
|
||||
#include <esp_bt_main.h> // ESP32 BLE
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <esp_gatts_api.h> // ESP32 BLE
|
||||
#include <esp_gattc_api.h> // ESP32 BLE
|
||||
#include <esp_gatt_common_api.h>// ESP32 BLE
|
||||
#include <esp_err.h> // ESP32 ESP-IDF
|
||||
#include <map> // Part of C++ Standard library
|
||||
#include <sstream> // Part of C++ Standard library
|
||||
#include <iomanip> // Part of C++ Standard library
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "esp32-hal-bt.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEDevice";
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Singletons for the BLEDevice.
|
||||
*/
|
||||
BLEServer* BLEDevice::m_pServer = nullptr;
|
||||
BLEScan* BLEDevice::m_pScan = nullptr;
|
||||
BLEClient* BLEDevice::m_pClient = nullptr;
|
||||
bool initialized = false;
|
||||
esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0;
|
||||
BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr;
|
||||
uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful
|
||||
BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr;
|
||||
uint16_t BLEDevice::m_appId = 0;
|
||||
std::map<uint16_t, conn_status_t> BLEDevice::m_connectedClientsMap;
|
||||
gap_event_handler BLEDevice::m_customGapHandler = nullptr;
|
||||
gattc_event_handler BLEDevice::m_customGattcHandler = nullptr;
|
||||
gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of a client.
|
||||
* @return A new instance of the client.
|
||||
*/
|
||||
/* STATIC */ BLEClient* BLEDevice::createClient() {
|
||||
ESP_LOGD(LOG_TAG, ">> createClient");
|
||||
#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig
|
||||
ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined");
|
||||
abort();
|
||||
#endif // CONFIG_GATTC_ENABLE
|
||||
m_pClient = new BLEClient();
|
||||
ESP_LOGD(LOG_TAG, "<< createClient");
|
||||
return m_pClient;
|
||||
} // createClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of a server.
|
||||
* @return A new instance of the server.
|
||||
*/
|
||||
/* STATIC */ BLEServer* BLEDevice::createServer() {
|
||||
ESP_LOGD(LOG_TAG, ">> createServer");
|
||||
#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig
|
||||
ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined");
|
||||
abort();
|
||||
#endif // CONFIG_GATTS_ENABLE
|
||||
m_pServer = new BLEServer();
|
||||
m_pServer->createApp(m_appId++);
|
||||
ESP_LOGD(LOG_TAG, "<< createServer");
|
||||
return m_pServer;
|
||||
} // createServer
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT server events.
|
||||
*
|
||||
* @param [in] event The event that has been newly received.
|
||||
* @param [in] gatts_if The connection to the GATT interface.
|
||||
* @param [in] param Parameters for the event.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gattServerEventHandler(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param
|
||||
) {
|
||||
ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gatts_if,
|
||||
BLEUtils::gattServerEventTypeToString(event).c_str());
|
||||
|
||||
BLEUtils::dumpGattServerEvent(event, gatts_if, param);
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
|
||||
|
||||
if (BLEDevice::m_pServer != nullptr) {
|
||||
BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
|
||||
if(m_customGattsHandler != nullptr) {
|
||||
m_customGattsHandler(event, gatts_if, param);
|
||||
}
|
||||
|
||||
} // gattServerEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT client events.
|
||||
*
|
||||
* Handler for the GATT client events.
|
||||
*
|
||||
* @param [in] event
|
||||
* @param [in] gattc_if
|
||||
* @param [in] param
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
|
||||
BLEUtils::dumpGattClientEvent(event, gattc_if, param);
|
||||
|
||||
switch(event) {
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
for(auto &myPair : BLEDevice::getPeerDevices(true)) {
|
||||
conn_status_t conn_status = (conn_status_t)myPair.second;
|
||||
if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){
|
||||
((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_customGattcHandler != nullptr) {
|
||||
m_customGattcHandler(event, gattc_if, param);
|
||||
}
|
||||
|
||||
|
||||
} // gattClientEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GAP events.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gapEventHandler(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param) {
|
||||
|
||||
BLEUtils::dumpGapEvent(event, param);
|
||||
|
||||
switch(event) {
|
||||
|
||||
case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey));
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: ");
|
||||
// esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda));
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
/*
|
||||
* TODO should we add white/black list comparison?
|
||||
*/
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
/* send the positive(true) security response to the peer device to accept the security request.
|
||||
If not accept the security request, should sent the security response with negative(false) accept value*/
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks!=nullptr){
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest());
|
||||
}
|
||||
else{
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
/*
|
||||
*
|
||||
*/
|
||||
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.
|
||||
//display the passkey number to the user to input it in the peer deivce within 30 seconds
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey);
|
||||
if(BLEDevice::m_securityCallbacks!=nullptr){
|
||||
BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_KEY_EVT:
|
||||
//shows the ble key type info share with peer device to the user.
|
||||
ESP_LOGD(LOG_TAG, "ESP_GAP_BLE_KEY_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
|
||||
if (BLEDevice::m_pClient != nullptr) {
|
||||
BLEDevice::m_pClient->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if (BLEDevice::m_pScan != nullptr) {
|
||||
BLEDevice::getScan()->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if(m_bleAdvertising != nullptr) {
|
||||
BLEDevice::getAdvertising()->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if(m_customGapHandler != nullptr) {
|
||||
BLEDevice::m_customGapHandler(event, param);
|
||||
}
|
||||
|
||||
} // gapEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the BLE device address.
|
||||
* @return The BLE device address.
|
||||
*/
|
||||
/* STATIC*/ BLEAddress BLEDevice::getAddress() {
|
||||
const uint8_t* bdAddr = esp_bt_dev_get_address();
|
||||
esp_bd_addr_t addr;
|
||||
memcpy(addr, bdAddr, sizeof(addr));
|
||||
return BLEAddress(addr);
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Scan object that we use for scanning.
|
||||
* @return The scanning object reference. This is a singleton object. The caller should not
|
||||
* try and release/delete it.
|
||||
*/
|
||||
/* STATIC */ BLEScan* BLEDevice::getScan() {
|
||||
//ESP_LOGD(LOG_TAG, ">> getScan");
|
||||
if (m_pScan == nullptr) {
|
||||
m_pScan = new BLEScan();
|
||||
//ESP_LOGD(LOG_TAG, " - creating a new scan object");
|
||||
}
|
||||
//ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan);
|
||||
return m_pScan;
|
||||
} // getScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of a characteristic of a service on a remote device.
|
||||
* @param [in] bdAddress
|
||||
* @param [in] serviceUUID
|
||||
* @param [in] characteristicUUID
|
||||
*/
|
||||
/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) {
|
||||
ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
BLEClient* pClient = createClient();
|
||||
pClient->connect(bdAddress);
|
||||
std::string ret = pClient->getValue(serviceUUID, characteristicUUID);
|
||||
pClient->disconnect();
|
||||
ESP_LOGD(LOG_TAG, "<< getValue");
|
||||
return ret;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the %BLE environment.
|
||||
* @param deviceName The device name of the device.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::init(std::string deviceName) {
|
||||
if(!initialized){
|
||||
initialized = true; // Set the initialization flag to ensure we are only initialized once.
|
||||
|
||||
esp_err_t errRc = ESP_OK;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!btStart()) {
|
||||
errRc = ESP_FAIL;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
errRc = ::nvs_flash_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#endif
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
errRc = esp_bt_controller_init(&bt_cfg);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
|
||||
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
|
||||
errRc = esp_bluedroid_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) {
|
||||
errRc = esp_bluedroid_enable();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig
|
||||
errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_GATTC_ENABLE
|
||||
|
||||
#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig
|
||||
errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_GATTS_ENABLE
|
||||
|
||||
errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
|
||||
errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
};
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
}
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue.
|
||||
} // init
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the transmission power.
|
||||
* The power level can be one of:
|
||||
* * ESP_PWR_LVL_N14
|
||||
* * ESP_PWR_LVL_N11
|
||||
* * ESP_PWR_LVL_N8
|
||||
* * ESP_PWR_LVL_N5
|
||||
* * ESP_PWR_LVL_N2
|
||||
* * ESP_PWR_LVL_P1
|
||||
* * ESP_PWR_LVL_P4
|
||||
* * ESP_PWR_LVL_P7
|
||||
* @param [in] powerLevel.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) {
|
||||
ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel);
|
||||
esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
};
|
||||
ESP_LOGD(LOG_TAG, "<< setPower");
|
||||
} // setPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of a characteristic of a service on a remote device.
|
||||
* @param [in] bdAddress
|
||||
* @param [in] serviceUUID
|
||||
* @param [in] characteristicUUID
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
BLEClient* pClient = createClient();
|
||||
pClient->connect(bdAddress);
|
||||
pClient->setValue(serviceUUID, characteristicUUID, value);
|
||||
pClient->disconnect();
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the nature of this device.
|
||||
* @return A string representation of the nature of this device.
|
||||
*/
|
||||
/* STATIC */ std::string BLEDevice::toString() {
|
||||
std::ostringstream oss;
|
||||
oss << "BD Address: " << getAddress().toString();
|
||||
return oss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add an entry to the BLE white list.
|
||||
* @param [in] address The address to add to the white list.
|
||||
*/
|
||||
void BLEDevice::whiteListAdd(BLEAddress address) {
|
||||
ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str());
|
||||
esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< whiteListAdd");
|
||||
} // whiteListAdd
|
||||
|
||||
|
||||
/**
|
||||
* @brief Remove an entry from the BLE white list.
|
||||
* @param [in] address The address to remove from the white list.
|
||||
*/
|
||||
void BLEDevice::whiteListRemove(BLEAddress address) {
|
||||
ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str());
|
||||
esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< whiteListRemove");
|
||||
} // whiteListRemove
|
||||
|
||||
/*
|
||||
* @brief Set encryption level that will be negotiated with peer device durng connection
|
||||
* @param [in] level Requested encryption level
|
||||
*/
|
||||
void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) {
|
||||
BLEDevice::m_securityLevel = level;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
|
||||
* @param [in] cllbacks Pointer to BLESecurityCallbacks class callback
|
||||
*/
|
||||
void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) {
|
||||
BLEDevice::m_securityCallbacks = callbacks;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer
|
||||
* @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517
|
||||
*/
|
||||
esp_err_t BLEDevice::setMTU(uint16_t mtu) {
|
||||
ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
|
||||
esp_err_t err = esp_ble_gatt_set_local_mtu(mtu);
|
||||
if (err == ESP_OK) {
|
||||
m_localMTU = mtu;
|
||||
} else {
|
||||
ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu);
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< setLocalMTU");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Get local MTU value set during mtu request or default value
|
||||
*/
|
||||
uint16_t BLEDevice::getMTU() {
|
||||
return m_localMTU;
|
||||
}
|
||||
|
||||
bool BLEDevice::getInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
BLEAdvertising* BLEDevice::getAdvertising() {
|
||||
if(m_bleAdvertising == nullptr) {
|
||||
m_bleAdvertising = new BLEAdvertising();
|
||||
ESP_LOGI(LOG_TAG, "create advertising");
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "get advertising");
|
||||
return m_bleAdvertising;
|
||||
}
|
||||
|
||||
void BLEDevice::startAdvertising() {
|
||||
ESP_LOGD(LOG_TAG, ">> startAdvertising");
|
||||
getAdvertising()->start();
|
||||
ESP_LOGD(LOG_TAG, "<< startAdvertising");
|
||||
} // startAdvertising
|
||||
|
||||
/* multi connect support */
|
||||
/* requires a little more work */
|
||||
std::map<uint16_t, conn_status_t> BLEDevice::getPeerDevices(bool _client) {
|
||||
return m_connectedClientsMap;
|
||||
}
|
||||
|
||||
BLEClient* BLEDevice::getClientByGattIf(uint16_t conn_id) {
|
||||
return (BLEClient*)m_connectedClientsMap.find(conn_id)->second.peer_device;
|
||||
}
|
||||
|
||||
void BLEDevice::updatePeerDevice(void* peer, bool _client, uint16_t conn_id) {
|
||||
ESP_LOGD(LOG_TAG, "update conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
|
||||
std::map<uint16_t, conn_status_t>::iterator it = m_connectedClientsMap.find(ESP_GATT_IF_NONE);
|
||||
if (it != m_connectedClientsMap.end()) {
|
||||
std::swap(m_connectedClientsMap[conn_id], it->second);
|
||||
m_connectedClientsMap.erase(it);
|
||||
}else{
|
||||
it = m_connectedClientsMap.find(conn_id);
|
||||
if (it != m_connectedClientsMap.end()) {
|
||||
conn_status_t _st = it->second;
|
||||
_st.peer_device = peer;
|
||||
std::swap(m_connectedClientsMap[conn_id], _st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
|
||||
ESP_LOGI(LOG_TAG, "add conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
|
||||
conn_status_t status = {
|
||||
.peer_device = peer,
|
||||
.connected = true,
|
||||
.mtu = 23
|
||||
};
|
||||
|
||||
m_connectedClientsMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
|
||||
}
|
||||
|
||||
void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) {
|
||||
ESP_LOGI(LOG_TAG, "remove: %d, GATT role %s", conn_id, _client?"client":"server");
|
||||
if(m_connectedClientsMap.find(conn_id) != m_connectedClientsMap.end())
|
||||
m_connectedClientsMap.erase(conn_id);
|
||||
}
|
||||
|
||||
/* multi connect support */
|
||||
|
||||
/**
|
||||
* @brief de-Initialize the %BLE environment.
|
||||
* @param release_memory release the internal BT stack memory
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::deinit(bool release_memory) {
|
||||
if (!initialized) return;
|
||||
|
||||
esp_bluedroid_disable();
|
||||
esp_bluedroid_deinit();
|
||||
esp_bt_controller_disable();
|
||||
esp_bt_controller_deinit();
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
if (release_memory) {
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it)
|
||||
} else {
|
||||
initialized = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGapHandler(gap_event_handler handler) {
|
||||
m_customGapHandler = handler;
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) {
|
||||
m_customGattcHandler = handler;
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) {
|
||||
m_customGattsHandler = handler;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
99
libraries/BLE/src/BLEDevice.h
Normal file
99
libraries/BLE/src/BLEDevice.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* BLEDevice.h
|
||||
*
|
||||
* Created on: Mar 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef MAIN_BLEDevice_H_
|
||||
#define MAIN_BLEDevice_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <esp_gattc_api.h> // ESP32 BLE
|
||||
#include <map> // Part of C++ STL
|
||||
#include <string>
|
||||
#include <esp_bt.h>
|
||||
|
||||
#include "BLEServer.h"
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEAddress.h"
|
||||
|
||||
/**
|
||||
* @brief BLE functions.
|
||||
*/
|
||||
typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
|
||||
typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param);
|
||||
typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
class BLEDevice {
|
||||
public:
|
||||
|
||||
static BLEClient* createClient(); // Create a new BLE client.
|
||||
static BLEServer* createServer(); // Cretae a new BLE server.
|
||||
static BLEAddress getAddress(); // Retrieve our own local BD address.
|
||||
static BLEScan* getScan(); // Get the scan object
|
||||
static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server.
|
||||
static void init(std::string deviceName); // Initialize the local BLE environment.
|
||||
static void setPower(esp_power_level_t powerLevel); // Set our power level.
|
||||
static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server.
|
||||
static std::string toString(); // Return a string representation of our device.
|
||||
static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list.
|
||||
static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list.
|
||||
static void setEncryptionLevel(esp_ble_sec_act_t level);
|
||||
static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks);
|
||||
static esp_err_t setMTU(uint16_t mtu);
|
||||
static uint16_t getMTU();
|
||||
static bool getInitialized(); // Returns the state of the device, is it initialized or not?
|
||||
/* move advertising to BLEDevice for saving ram and flash in beacons */
|
||||
static BLEAdvertising* getAdvertising();
|
||||
static void startAdvertising();
|
||||
static uint16_t m_appId;
|
||||
/* multi connect */
|
||||
static std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
|
||||
static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
|
||||
static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id);
|
||||
static void removePeerDevice(uint16_t conn_id, bool client);
|
||||
static BLEClient* getClientByGattIf(uint16_t conn_id);
|
||||
static void setCustomGapHandler(gap_event_handler handler);
|
||||
static void setCustomGattcHandler(gattc_event_handler handler);
|
||||
static void setCustomGattsHandler(gatts_event_handler handler);
|
||||
static void deinit(bool release_memory = false);
|
||||
static uint16_t m_localMTU;
|
||||
static esp_ble_sec_act_t m_securityLevel;
|
||||
|
||||
private:
|
||||
static BLEServer* m_pServer;
|
||||
static BLEScan* m_pScan;
|
||||
static BLEClient* m_pClient;
|
||||
static BLESecurityCallbacks* m_securityCallbacks;
|
||||
static BLEAdvertising* m_bleAdvertising;
|
||||
static esp_gatt_if_t getGattcIF();
|
||||
static std::map<uint16_t, conn_status_t> m_connectedClientsMap;
|
||||
|
||||
static void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param);
|
||||
|
||||
static void gattServerEventHandler(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
static void gapEventHandler(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
|
||||
public:
|
||||
/* custom gap and gatt handlers for flexibility */
|
||||
static gap_event_handler m_customGapHandler;
|
||||
static gattc_event_handler m_customGattcHandler;
|
||||
static gatts_event_handler m_customGattsHandler;
|
||||
|
||||
}; // class BLE
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDevice_H_ */
|
150
libraries/BLE/src/BLEEddystoneTLM.cpp
Normal file
150
libraries/BLE/src/BLEEddystoneTLM.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* BLEEddystoneTLM.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <esp_log.h>
|
||||
#include "BLEEddystoneTLM.h"
|
||||
|
||||
static const char LOG_TAG[] = "BLEEddystoneTLM";
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
|
||||
#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24))
|
||||
|
||||
BLEEddystoneTLM::BLEEddystoneTLM() {
|
||||
beaconUUID = 0xFEAA;
|
||||
m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE;
|
||||
m_eddystoneData.version = 0;
|
||||
m_eddystoneData.volt = 3300; // 3300mV = 3.3V
|
||||
m_eddystoneData.temp = (uint16_t) ((float) 23.00);
|
||||
m_eddystoneData.advCount = 0;
|
||||
m_eddystoneData.tmil = 0;
|
||||
} // BLEEddystoneTLM
|
||||
|
||||
std::string BLEEddystoneTLM::getData() {
|
||||
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
|
||||
} // getData
|
||||
|
||||
BLEUUID BLEEddystoneTLM::getUUID() {
|
||||
return BLEUUID(beaconUUID);
|
||||
} // getUUID
|
||||
|
||||
uint8_t BLEEddystoneTLM::getVersion() {
|
||||
return m_eddystoneData.version;
|
||||
} // getVersion
|
||||
|
||||
uint16_t BLEEddystoneTLM::getVolt() {
|
||||
return m_eddystoneData.volt;
|
||||
} // getVolt
|
||||
|
||||
float BLEEddystoneTLM::getTemp() {
|
||||
return (float)m_eddystoneData.temp;
|
||||
} // getTemp
|
||||
|
||||
uint32_t BLEEddystoneTLM::getCount() {
|
||||
return m_eddystoneData.advCount;
|
||||
} // getCount
|
||||
|
||||
uint32_t BLEEddystoneTLM::getTime() {
|
||||
return m_eddystoneData.tmil;
|
||||
} // getTime
|
||||
|
||||
std::string BLEEddystoneTLM::toString() {
|
||||
std::stringstream ss;
|
||||
std::string out = "";
|
||||
uint32_t rawsec;
|
||||
ss << "Version ";
|
||||
ss << std::dec << m_eddystoneData.version;
|
||||
ss << "\n";
|
||||
|
||||
ss << "Battery Voltage ";
|
||||
ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt);
|
||||
ss << " mV\n";
|
||||
|
||||
ss << "Temperature ";
|
||||
ss << (float) m_eddystoneData.temp;
|
||||
ss << " °C\n";
|
||||
|
||||
ss << "Adv. Count ";
|
||||
ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
|
||||
|
||||
ss << "\n";
|
||||
|
||||
ss << "Time ";
|
||||
|
||||
rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
|
||||
std::stringstream buffstream;
|
||||
buffstream << "0000";
|
||||
buffstream << std::dec << rawsec / 864000;
|
||||
std::string buff = buffstream.str();
|
||||
|
||||
ss << buff.substr(buff.length() - 4, buff.length());
|
||||
ss << ".";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 36000) % 24;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length()-2, buff.length());
|
||||
ss << ":";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 600) % 60;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length() - 2, buff.length());
|
||||
ss << ":";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 10) % 60;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length() - 2, buff.length());
|
||||
ss << "\n";
|
||||
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEEddystoneTLM::setData(std::string data) {
|
||||
if (data.length() != sizeof(m_eddystoneData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData));
|
||||
return;
|
||||
}
|
||||
memcpy(&m_eddystoneData, data.data(), data.length());
|
||||
} // setData
|
||||
|
||||
void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) {
|
||||
beaconUUID = l_uuid.getNative()->uuid.uuid16;
|
||||
} // setUUID
|
||||
|
||||
void BLEEddystoneTLM::setVersion(uint8_t version) {
|
||||
m_eddystoneData.version = version;
|
||||
} // setVersion
|
||||
|
||||
void BLEEddystoneTLM::setVolt(uint16_t volt) {
|
||||
m_eddystoneData.volt = volt;
|
||||
} // setVolt
|
||||
|
||||
void BLEEddystoneTLM::setTemp(float temp) {
|
||||
m_eddystoneData.temp = (uint16_t)temp;
|
||||
} // setTemp
|
||||
|
||||
void BLEEddystoneTLM::setCount(uint32_t advCount) {
|
||||
m_eddystoneData.advCount = advCount;
|
||||
} // setCount
|
||||
|
||||
void BLEEddystoneTLM::setTime(uint32_t tmil) {
|
||||
m_eddystoneData.tmil = tmil;
|
||||
} // setTime
|
||||
|
||||
#endif
|
51
libraries/BLE/src/BLEEddystoneTLM.h
Normal file
51
libraries/BLE/src/BLEEddystoneTLM.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* BLEEddystoneTLM.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
|
||||
#ifndef _BLEEddystoneTLM_H_
|
||||
#define _BLEEddystoneTLM_H_
|
||||
#include "BLEUUID.h"
|
||||
|
||||
#define EDDYSTONE_TLM_FRAME_TYPE 0x20
|
||||
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://github.com/google/eddystone
|
||||
*/
|
||||
class BLEEddystoneTLM {
|
||||
public:
|
||||
BLEEddystoneTLM();
|
||||
std::string getData();
|
||||
BLEUUID getUUID();
|
||||
uint8_t getVersion();
|
||||
uint16_t getVolt();
|
||||
float getTemp();
|
||||
uint32_t getCount();
|
||||
uint32_t getTime();
|
||||
std::string toString();
|
||||
void setData(std::string data);
|
||||
void setUUID(BLEUUID l_uuid);
|
||||
void setVersion(uint8_t version);
|
||||
void setVolt(uint16_t volt);
|
||||
void setTemp(float temp);
|
||||
void setCount(uint32_t advCount);
|
||||
void setTime(uint32_t tmil);
|
||||
|
||||
private:
|
||||
uint16_t beaconUUID;
|
||||
struct {
|
||||
uint8_t frameType;
|
||||
uint8_t version;
|
||||
uint16_t volt;
|
||||
uint16_t temp;
|
||||
uint32_t advCount;
|
||||
uint32_t tmil;
|
||||
} __attribute__((packed)) m_eddystoneData;
|
||||
|
||||
}; // BLEEddystoneTLM
|
||||
|
||||
#endif /* _BLEEddystoneTLM_H_ */
|
148
libraries/BLE/src/BLEEddystoneURL.cpp
Normal file
148
libraries/BLE/src/BLEEddystoneURL.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* BLEEddystoneURL.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include "BLEEddystoneURL.h"
|
||||
|
||||
static const char LOG_TAG[] = "BLEEddystoneURL";
|
||||
|
||||
BLEEddystoneURL::BLEEddystoneURL() {
|
||||
beaconUUID = 0xFEAA;
|
||||
lengthURL = 0;
|
||||
m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE;
|
||||
m_eddystoneData.advertisedTxPower = 0;
|
||||
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
|
||||
} // BLEEddystoneURL
|
||||
|
||||
std::string BLEEddystoneURL::getData() {
|
||||
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
|
||||
} // getData
|
||||
|
||||
BLEUUID BLEEddystoneURL::getUUID() {
|
||||
return BLEUUID(beaconUUID);
|
||||
} // getUUID
|
||||
|
||||
int8_t BLEEddystoneURL::getPower() {
|
||||
return m_eddystoneData.advertisedTxPower;
|
||||
} // getPower
|
||||
|
||||
std::string BLEEddystoneURL::getURL() {
|
||||
return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url));
|
||||
} // getURL
|
||||
|
||||
std::string BLEEddystoneURL::getDecodedURL() {
|
||||
std::string decodedURL = "";
|
||||
|
||||
switch (m_eddystoneData.url[0]) {
|
||||
case 0x00:
|
||||
decodedURL += "http://www.";
|
||||
break;
|
||||
case 0x01:
|
||||
decodedURL += "https://www.";
|
||||
break;
|
||||
case 0x02:
|
||||
decodedURL += "http://";
|
||||
break;
|
||||
case 0x03:
|
||||
decodedURL += "https://";
|
||||
break;
|
||||
default:
|
||||
decodedURL += m_eddystoneData.url[0];
|
||||
}
|
||||
|
||||
for (int i = 1; i < lengthURL; i++) {
|
||||
if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) {
|
||||
decodedURL += m_eddystoneData.url[i];
|
||||
} else {
|
||||
switch (m_eddystoneData.url[i]) {
|
||||
case 0x00:
|
||||
decodedURL += ".com/";
|
||||
break;
|
||||
case 0x01:
|
||||
decodedURL += ".org/";
|
||||
break;
|
||||
case 0x02:
|
||||
decodedURL += ".edu/";
|
||||
break;
|
||||
case 0x03:
|
||||
decodedURL += ".net/";
|
||||
break;
|
||||
case 0x04:
|
||||
decodedURL += ".info/";
|
||||
break;
|
||||
case 0x05:
|
||||
decodedURL += ".biz/";
|
||||
break;
|
||||
case 0x06:
|
||||
decodedURL += ".gov/";
|
||||
break;
|
||||
case 0x07:
|
||||
decodedURL += ".com";
|
||||
break;
|
||||
case 0x08:
|
||||
decodedURL += ".org";
|
||||
break;
|
||||
case 0x09:
|
||||
decodedURL += ".edu";
|
||||
break;
|
||||
case 0x0A:
|
||||
decodedURL += ".net";
|
||||
break;
|
||||
case 0x0B:
|
||||
decodedURL += ".info";
|
||||
break;
|
||||
case 0x0C:
|
||||
decodedURL += ".biz";
|
||||
break;
|
||||
case 0x0D:
|
||||
decodedURL += ".gov";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return decodedURL;
|
||||
} // getDecodedURL
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEEddystoneURL::setData(std::string data) {
|
||||
if (data.length() > sizeof(m_eddystoneData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData));
|
||||
return;
|
||||
}
|
||||
memset(&m_eddystoneData, 0, sizeof(m_eddystoneData));
|
||||
memcpy(&m_eddystoneData, data.data(), data.length());
|
||||
lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url));
|
||||
} // setData
|
||||
|
||||
void BLEEddystoneURL::setUUID(BLEUUID l_uuid) {
|
||||
beaconUUID = l_uuid.getNative()->uuid.uuid16;
|
||||
} // setUUID
|
||||
|
||||
void BLEEddystoneURL::setPower(int8_t advertisedTxPower) {
|
||||
m_eddystoneData.advertisedTxPower = advertisedTxPower;
|
||||
} // setPower
|
||||
|
||||
void BLEEddystoneURL::setURL(std::string url) {
|
||||
if (url.length() > sizeof(m_eddystoneData.url)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url));
|
||||
return;
|
||||
}
|
||||
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
|
||||
memcpy(m_eddystoneData.url, url.data(), url.length());
|
||||
lengthURL = url.length();
|
||||
} // setURL
|
||||
|
||||
|
||||
#endif
|
43
libraries/BLE/src/BLEEddystoneURL.h
Normal file
43
libraries/BLE/src/BLEEddystoneURL.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* BLEEddystoneURL.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
|
||||
#ifndef _BLEEddystoneURL_H_
|
||||
#define _BLEEddystoneURL_H_
|
||||
#include "BLEUUID.h"
|
||||
|
||||
#define EDDYSTONE_URL_FRAME_TYPE 0x10
|
||||
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://github.com/google/eddystone
|
||||
*/
|
||||
class BLEEddystoneURL {
|
||||
public:
|
||||
BLEEddystoneURL();
|
||||
std::string getData();
|
||||
BLEUUID getUUID();
|
||||
int8_t getPower();
|
||||
std::string getURL();
|
||||
std::string getDecodedURL();
|
||||
void setData(std::string data);
|
||||
void setUUID(BLEUUID l_uuid);
|
||||
void setPower(int8_t advertisedTxPower);
|
||||
void setURL(std::string url);
|
||||
|
||||
private:
|
||||
uint16_t beaconUUID;
|
||||
uint8_t lengthURL;
|
||||
struct {
|
||||
uint8_t frameType;
|
||||
int8_t advertisedTxPower;
|
||||
uint8_t url[16];
|
||||
} __attribute__((packed)) m_eddystoneData;
|
||||
|
||||
}; // BLEEddystoneURL
|
||||
|
||||
#endif /* _BLEEddystoneURL_H_ */
|
9
libraries/BLE/src/BLEExceptions.cpp
Normal file
9
libraries/BLE/src/BLEExceptions.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* BLExceptions.cpp
|
||||
*
|
||||
* Created on: Nov 27, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "BLEExceptions.h"
|
||||
|
31
libraries/BLE/src/BLEExceptions.h
Normal file
31
libraries/BLE/src/BLEExceptions.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* BLExceptions.h
|
||||
*
|
||||
* Created on: Nov 27, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_CXX_EXCEPTIONS != 1
|
||||
#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
|
||||
|
||||
class BLEDisconnectedException : public std::exception {
|
||||
const char* what() const throw () {
|
||||
return "BLE Disconnected";
|
||||
}
|
||||
};
|
||||
|
||||
class BLEUuidNotFoundException : public std::exception {
|
||||
const char* what() const throw () {
|
||||
return "No such UUID";
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */
|
243
libraries/BLE/src/BLEHIDDevice.cpp
Normal file
243
libraries/BLE/src/BLEHIDDevice.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* BLEHIDDevice.cpp
|
||||
*
|
||||
* Created on: Jan 03, 2018
|
||||
* Author: chegewara
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEHIDDevice.h"
|
||||
#include "BLE2904.h"
|
||||
|
||||
|
||||
BLEHIDDevice::BLEHIDDevice(BLEServer* server) {
|
||||
/*
|
||||
* Here we create mandatory services described in bluetooth specification
|
||||
*/
|
||||
m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a));
|
||||
m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40);
|
||||
m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f));
|
||||
|
||||
/*
|
||||
* Mandatory characteristic for device info service
|
||||
*/
|
||||
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
/*
|
||||
* Mandatory characteristics for HID service
|
||||
*/
|
||||
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ);
|
||||
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ);
|
||||
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR);
|
||||
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
/*
|
||||
* Mandatory battery level characteristic with notification and presence descriptor
|
||||
*/
|
||||
BLE2904* batteryLevelDescriptor = new BLE2904();
|
||||
batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
|
||||
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor);
|
||||
m_batteryLevelCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
/*
|
||||
* This value is setup here because its default value in most usage cases, its very rare to use boot mode
|
||||
* and we want to simplify library using as much as possible
|
||||
*/
|
||||
const uint8_t pMode[] = { 0x01 };
|
||||
protocolMode()->setValue((uint8_t*) pMode, 1);
|
||||
}
|
||||
|
||||
BLEHIDDevice::~BLEHIDDevice() {
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
|
||||
m_reportMapCharacteristic->setValue(map, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service
|
||||
*/
|
||||
void BLEHIDDevice::startServices() {
|
||||
m_deviceInfoService->start();
|
||||
m_hidService->start();
|
||||
m_batteryService->start();
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create manufacturer characteristic (this characteristic is optional)
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::manufacturer() {
|
||||
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ);
|
||||
return m_manufacturerCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Set manufacturer name
|
||||
* @param [in] name manufacturer name
|
||||
*/
|
||||
void BLEHIDDevice::manufacturer(std::string name) {
|
||||
m_manufacturerCharacteristic->setValue(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
|
||||
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
|
||||
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
|
||||
uint8_t info[] = { 0x11, 0x1, country, flags };
|
||||
m_hidInfoCharacteristic->setValue(info, sizeof(info));
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create input report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID input report ID, the same as in report map for input object related to created characteristic
|
||||
* @return pointer to new input report characteristic
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) {
|
||||
BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
|
||||
BLE2902* p2902 = new BLE2902();
|
||||
inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x01 };
|
||||
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
inputReportCharacteristic->addDescriptor(p2902);
|
||||
inputReportCharacteristic->addDescriptor(inputReportDescriptor);
|
||||
|
||||
return inputReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create output report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic
|
||||
* @return Pointer to new output report characteristic
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) {
|
||||
BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
|
||||
BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
|
||||
outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x02 };
|
||||
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
outputReportCharacteristic->addDescriptor(outputReportDescriptor);
|
||||
|
||||
return outputReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic
|
||||
* @return Pointer to new feature report characteristic
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) {
|
||||
BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
|
||||
BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
|
||||
|
||||
featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x03 };
|
||||
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
featureReportCharacteristic->addDescriptor(featureReportDescriptor);
|
||||
|
||||
return featureReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::bootInput() {
|
||||
BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY);
|
||||
bootInputCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
return bootInputCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::bootOutput() {
|
||||
return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::hidControl() {
|
||||
return m_hidControlCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLECharacteristic* BLEHIDDevice::protocolMode() {
|
||||
return m_protocolModeCharacteristic;
|
||||
}
|
||||
|
||||
void BLEHIDDevice::setBatteryLevel(uint8_t level) {
|
||||
m_batteryLevelCharacteristic->setValue(&level, 1);
|
||||
}
|
||||
/*
|
||||
* @brief Returns battery level characteristic
|
||||
* @ return battery level characteristic
|
||||
*//*
|
||||
BLECharacteristic* BLEHIDDevice::batteryLevel() {
|
||||
return m_batteryLevelCharacteristic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::reportMap() {
|
||||
return m_reportMapCharacteristic;
|
||||
}
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::pnp() {
|
||||
return m_pnpCharacteristic;
|
||||
}
|
||||
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::hidInfo() {
|
||||
return m_hidInfoCharacteristic;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLEService* BLEHIDDevice::deviceInfo() {
|
||||
return m_deviceInfoService;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLEService* BLEHIDDevice::hidService() {
|
||||
return m_hidService;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
BLEService* BLEHIDDevice::batteryService() {
|
||||
return m_batteryService;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
|
75
libraries/BLE/src/BLEHIDDevice.h
Normal file
75
libraries/BLE/src/BLEHIDDevice.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* BLEHIDDevice.h
|
||||
*
|
||||
* Created on: Jan 03, 2018
|
||||
* Author: chegewara
|
||||
*/
|
||||
|
||||
#ifndef _BLEHIDDEVICE_H_
|
||||
#define _BLEHIDDEVICE_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEDescriptor.h"
|
||||
#include "BLE2902.h"
|
||||
#include "HIDTypes.h"
|
||||
|
||||
#define GENERIC_HID 0x03C0
|
||||
#define HID_KEYBOARD 0x03C1
|
||||
#define HID_MOUSE 0x03C2
|
||||
#define HID_JOYSTICK 0x03C3
|
||||
#define HID_GAMEPAD 0x03C4
|
||||
#define HID_TABLET 0x03C5
|
||||
#define HID_CARD_READER 0x03C6
|
||||
#define HID_DIGITAL_PEN 0x03C7
|
||||
#define HID_BARCODE 0x03C8
|
||||
|
||||
class BLEHIDDevice {
|
||||
public:
|
||||
BLEHIDDevice(BLEServer*);
|
||||
virtual ~BLEHIDDevice();
|
||||
|
||||
void reportMap(uint8_t* map, uint16_t);
|
||||
void startServices();
|
||||
|
||||
BLEService* deviceInfo();
|
||||
BLEService* hidService();
|
||||
BLEService* batteryService();
|
||||
|
||||
BLECharacteristic* manufacturer();
|
||||
void manufacturer(std::string name);
|
||||
//BLECharacteristic* pnp();
|
||||
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
|
||||
//BLECharacteristic* hidInfo();
|
||||
void hidInfo(uint8_t country, uint8_t flags);
|
||||
//BLECharacteristic* batteryLevel();
|
||||
void setBatteryLevel(uint8_t level);
|
||||
|
||||
|
||||
//BLECharacteristic* reportMap();
|
||||
BLECharacteristic* hidControl();
|
||||
BLECharacteristic* inputReport(uint8_t reportID);
|
||||
BLECharacteristic* outputReport(uint8_t reportID);
|
||||
BLECharacteristic* featureReport(uint8_t reportID);
|
||||
BLECharacteristic* protocolMode();
|
||||
BLECharacteristic* bootInput();
|
||||
BLECharacteristic* bootOutput();
|
||||
|
||||
private:
|
||||
BLEService* m_deviceInfoService; //0x180a
|
||||
BLEService* m_hidService; //0x1812
|
||||
BLEService* m_batteryService = 0; //0x180f
|
||||
|
||||
BLECharacteristic* m_manufacturerCharacteristic; //0x2a29
|
||||
BLECharacteristic* m_pnpCharacteristic; //0x2a50
|
||||
BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
|
||||
BLECharacteristic* m_reportMapCharacteristic; //0x2a4b
|
||||
BLECharacteristic* m_hidControlCharacteristic; //0x2a4c
|
||||
BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
|
||||
BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
|
||||
};
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* _BLEHIDDEVICE_H_ */
|
588
libraries/BLE/src/BLERemoteCharacteristic.cpp
Normal file
588
libraries/BLE/src/BLERemoteCharacteristic.cpp
Normal file
@ -0,0 +1,588 @@
|
||||
/*
|
||||
* BLERemoteCharacteristic.cpp
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "BLERemoteCharacteristic.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#include <sstream>
|
||||
#include "BLEExceptions.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "BLERemoteDescriptor.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param [in] handle The BLE server side handle of this characteristic.
|
||||
* @param [in] uuid The UUID of this characteristic.
|
||||
* @param [in] charProp The properties of this characteristic.
|
||||
* @param [in] pRemoteService A reference to the remote service to which this remote characteristic pertains.
|
||||
*/
|
||||
BLERemoteCharacteristic::BLERemoteCharacteristic(
|
||||
uint16_t handle,
|
||||
BLEUUID uuid,
|
||||
esp_gatt_char_prop_t charProp,
|
||||
BLERemoteService* pRemoteService) {
|
||||
ESP_LOGD(LOG_TAG, ">> BLERemoteCharacteristic: handle: %d 0x%d, uuid: %s", handle, handle, uuid.toString().c_str());
|
||||
m_handle = handle;
|
||||
m_uuid = uuid;
|
||||
m_charProp = charProp;
|
||||
m_pRemoteService = pRemoteService;
|
||||
m_notifyCallback = nullptr;
|
||||
|
||||
retrieveDescriptors(); // Get the descriptors for this characteristic
|
||||
ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic");
|
||||
} // BLERemoteCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
*@brief Destructor.
|
||||
*/
|
||||
BLERemoteCharacteristic::~BLERemoteCharacteristic() {
|
||||
removeDescriptors(); // Release resources for any descriptor information we may have allocated.
|
||||
} // ~BLERemoteCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support broadcasting?
|
||||
* @return True if the characteristic supports broadcasting.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canBroadcast() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0;
|
||||
} // canBroadcast
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support indications?
|
||||
* @return True if the characteristic supports indications.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canIndicate() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0;
|
||||
} // canIndicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support notifications?
|
||||
* @return True if the characteristic supports notifications.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canNotify() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0;
|
||||
} // canNotify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support reading?
|
||||
* @return True if the characteristic supports reading.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canRead() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0;
|
||||
} // canRead
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support writing?
|
||||
* @return True if the characteristic supports writing.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canWrite() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0;
|
||||
} // canWrite
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the characteristic support writing with no response?
|
||||
* @return True if the characteristic supports writing with no response.
|
||||
*/
|
||||
bool BLERemoteCharacteristic::canWriteNoResponse() {
|
||||
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0;
|
||||
} // canWriteNoResponse
|
||||
|
||||
|
||||
/*
|
||||
static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
|
||||
if (id1.id.inst_id != id2.id.inst_id) {
|
||||
return false;
|
||||
}
|
||||
if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // compareSrvcId
|
||||
*/
|
||||
|
||||
/*
|
||||
static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) {
|
||||
if (id1.inst_id != id2.inst_id) {
|
||||
return false;
|
||||
}
|
||||
if (!BLEUUID(id1.uuid).equals(BLEUUID(id2.uuid))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // compareCharId
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT Client events.
|
||||
* When an event arrives for a GATT client we give this characteristic the opportunity to
|
||||
* take a look at it to see if there is interest in it.
|
||||
* @param [in] event The type of event.
|
||||
* @param [in] gattc_if The interface on which the event was received.
|
||||
* @param [in] evtParam Payload data for the event.
|
||||
* @returns N/A
|
||||
*/
|
||||
void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) {
|
||||
switch(event) {
|
||||
// ESP_GATTC_NOTIFY_EVT
|
||||
//
|
||||
// notify
|
||||
// - uint16_t conn_id - The connection identifier of the server.
|
||||
// - esp_bd_addr_t remote_bda - The device address of the BLE server.
|
||||
// - uint16_t handle - The handle of the characteristic for which the event is being received.
|
||||
// - uint16_t value_len - The length of the received data.
|
||||
// - uint8_t* value - The received data.
|
||||
// - bool is_notify - True if this is a notify, false if it is an indicate.
|
||||
//
|
||||
// We have received a notification event which means that the server wishes us to know about a notification
|
||||
// piece of data. What we must now do is find the characteristic with the associated handle and then
|
||||
// invoke its notification callback (if it has one).
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (evtParam->notify.handle != getHandle()) break;
|
||||
if (m_notifyCallback != nullptr) {
|
||||
ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str());
|
||||
m_notifyCallback(this, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify);
|
||||
} // End we have a callback function ...
|
||||
break;
|
||||
} // ESP_GATTC_NOTIFY_EVT
|
||||
|
||||
// ESP_GATTC_READ_CHAR_EVT
|
||||
// This event indicates that the server has responded to the read request.
|
||||
//
|
||||
// read:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t handle
|
||||
// - uint8_t* value
|
||||
// - uint16_t value_len
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
// If this event is not for us, then nothing further to do.
|
||||
if (evtParam->read.handle != getHandle()) break;
|
||||
|
||||
// At this point, we have determined that the event is for us, so now we save the value
|
||||
// and unlock the semaphore to ensure that the requestor of the data can continue.
|
||||
if (evtParam->read.status == ESP_GATT_OK) {
|
||||
m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len);
|
||||
if(m_rawData != nullptr) free(m_rawData);
|
||||
m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t));
|
||||
memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len);
|
||||
} else {
|
||||
m_value = "";
|
||||
}
|
||||
|
||||
m_semaphoreReadCharEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_READ_CHAR_EVT
|
||||
|
||||
// ESP_GATTC_REG_FOR_NOTIFY_EVT
|
||||
//
|
||||
// reg_for_notify:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t handle
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
// If the request is not for this BLERemoteCharacteristic then move on to the next.
|
||||
if (evtParam->reg_for_notify.handle != getHandle()) break;
|
||||
|
||||
// We have processed the notify registration and can unlock the semaphore.
|
||||
m_semaphoreRegForNotifyEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_REG_FOR_NOTIFY_EVT
|
||||
|
||||
// ESP_GATTC_UNREG_FOR_NOTIFY_EVT
|
||||
//
|
||||
// unreg_for_notify:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t handle
|
||||
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
|
||||
if (evtParam->unreg_for_notify.handle != getHandle()) break;
|
||||
// We have processed the notify un-registration and can unlock the semaphore.
|
||||
m_semaphoreRegForNotifyEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_UNREG_FOR_NOTIFY_EVT:
|
||||
|
||||
// ESP_GATTC_WRITE_CHAR_EVT
|
||||
//
|
||||
// write:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t handle
|
||||
case ESP_GATTC_WRITE_CHAR_EVT: {
|
||||
// Determine if this event is for us and, if not, pass onwards.
|
||||
if (evtParam->write.handle != getHandle()) break;
|
||||
|
||||
// There is nothing further we need to do here. This is merely an indication
|
||||
// that the write has completed and we can unlock the caller.
|
||||
m_semaphoreWriteCharEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_WRITE_CHAR_EVT
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
} // End switch
|
||||
}; // gattClientEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Populate the descriptors (if any) for this characteristic.
|
||||
*/
|
||||
void BLERemoteCharacteristic::retrieveDescriptors() {
|
||||
ESP_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
|
||||
|
||||
removeDescriptors(); // Remove any existing descriptors.
|
||||
|
||||
// Loop over each of the descriptors within the service associated with this characteristic.
|
||||
// For each descriptor we find, create a BLERemoteDescriptor instance.
|
||||
uint16_t offset = 0;
|
||||
esp_gattc_descr_elem_t result;
|
||||
while(true) {
|
||||
uint16_t count = 10;
|
||||
esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr(
|
||||
getRemoteService()->getClient()->getGattcIf(),
|
||||
getRemoteService()->getClient()->getConnId(),
|
||||
getHandle(),
|
||||
&result,
|
||||
&count,
|
||||
offset
|
||||
);
|
||||
|
||||
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != ESP_GATT_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_descr: %s", BLEUtils::gattStatusToString(status).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0) break;
|
||||
|
||||
ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str());
|
||||
|
||||
// We now have a new characteristic ... let us add that to our set of known characteristics
|
||||
BLERemoteDescriptor* pNewRemoteDescriptor = new BLERemoteDescriptor(
|
||||
result.handle,
|
||||
BLEUUID(result.uuid),
|
||||
this
|
||||
);
|
||||
|
||||
m_descriptorMap.insert(std::pair<std::string, BLERemoteDescriptor*>(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor));
|
||||
|
||||
offset++;
|
||||
} // while true
|
||||
//m_haveCharacteristics = true; // Remember that we have received the characteristics.
|
||||
ESP_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", offset);
|
||||
} // getDescriptors
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the map of descriptors keyed by UUID.
|
||||
*/
|
||||
std::map<std::string, BLERemoteDescriptor*>* BLERemoteCharacteristic::getDescriptors() {
|
||||
return &m_descriptorMap;
|
||||
} // getDescriptors
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the handle for this characteristic.
|
||||
* @return The handle for this characteristic.
|
||||
*/
|
||||
uint16_t BLERemoteCharacteristic::getHandle() {
|
||||
//ESP_LOGD(LOG_TAG, ">> getHandle: Characteristic: %s", getUUID().toString().c_str());
|
||||
//ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", m_handle, m_handle);
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
|
||||
* @param [in] uuid The UUID of the descriptor to find.
|
||||
* @return The Remote descriptor (if present) or null if not present.
|
||||
*/
|
||||
BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) {
|
||||
ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
|
||||
std::string v = uuid.toString();
|
||||
for (auto &myPair : m_descriptorMap) {
|
||||
if (myPair.first == v) {
|
||||
ESP_LOGD(LOG_TAG, "<< getDescriptor: found");
|
||||
return myPair.second;
|
||||
}
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< getDescriptor: Not found");
|
||||
return nullptr;
|
||||
} // getDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the remote service associated with this characteristic.
|
||||
* @return The remote service associated with this characteristic.
|
||||
*/
|
||||
BLERemoteService* BLERemoteCharacteristic::getRemoteService() {
|
||||
return m_pRemoteService;
|
||||
} // getRemoteService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID for this characteristic.
|
||||
* @return The UUID for this characteristic.
|
||||
*/
|
||||
BLEUUID BLERemoteCharacteristic::getUUID() {
|
||||
return m_uuid;
|
||||
} // getUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read an unsigned 16 bit value
|
||||
* @return The unsigned 16 bit value.
|
||||
*/
|
||||
uint16_t BLERemoteCharacteristic::readUInt16() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 2) {
|
||||
return *(uint16_t*)(value.data());
|
||||
}
|
||||
return 0;
|
||||
} // readUInt16
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read an unsigned 32 bit value.
|
||||
* @return the unsigned 32 bit value.
|
||||
*/
|
||||
uint32_t BLERemoteCharacteristic::readUInt32() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 4) {
|
||||
return *(uint32_t*)(value.data());
|
||||
}
|
||||
return 0;
|
||||
} // readUInt32
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read a byte value
|
||||
* @return The value as a byte
|
||||
*/
|
||||
uint8_t BLERemoteCharacteristic::readUInt8() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 1) {
|
||||
return (uint8_t)value[0];
|
||||
}
|
||||
return 0;
|
||||
} // readUInt8
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read the value of the remote characteristic.
|
||||
* @return The value of the remote characteristic.
|
||||
*/
|
||||
std::string BLERemoteCharacteristic::readValue() {
|
||||
ESP_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());
|
||||
|
||||
// Check to see that we are connected.
|
||||
if (!getRemoteService()->getClient()->isConnected()) {
|
||||
ESP_LOGE(LOG_TAG, "Disconnected");
|
||||
throw BLEDisconnectedException();
|
||||
}
|
||||
|
||||
m_semaphoreReadCharEvt.take("readValue");
|
||||
|
||||
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
|
||||
// This is an asynchronous request which means that we must block waiting for the response
|
||||
// to become available.
|
||||
esp_err_t errRc = ::esp_ble_gattc_read_char(
|
||||
m_pRemoteService->getClient()->getGattcIf(),
|
||||
m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server
|
||||
getHandle(), // The handle of this characteristic
|
||||
ESP_GATT_AUTH_REQ_NONE); // Security
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return "";
|
||||
}
|
||||
|
||||
// Block waiting for the event that indicates that the read has completed. When it has, the std::string found
|
||||
// in m_value will contain our data.
|
||||
m_semaphoreReadCharEvt.wait("readValue");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
|
||||
return m_value;
|
||||
} // readValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register for notifications.
|
||||
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
|
||||
* unregistering a notification.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications) {
|
||||
ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str());
|
||||
|
||||
m_notifyCallback = notifyCallback; // Save the notification callback.
|
||||
|
||||
m_semaphoreRegForNotifyEvt.take("registerForNotify");
|
||||
|
||||
if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration.
|
||||
esp_err_t errRc = ::esp_ble_gattc_register_for_notify(
|
||||
m_pRemoteService->getClient()->getGattcIf(),
|
||||
*m_pRemoteService->getClient()->getPeerAddress().getNative(),
|
||||
getHandle()
|
||||
);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
|
||||
uint8_t val[] = {0x01, 0x00};
|
||||
if(!notifications) val[0] = 0x02;
|
||||
BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902));
|
||||
desc->writeValue(val, 2);
|
||||
} // End Register
|
||||
else { // If we weren't passed a callback function, then this is an unregistration.
|
||||
esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify(
|
||||
m_pRemoteService->getClient()->getGattcIf(),
|
||||
*m_pRemoteService->getClient()->getPeerAddress().getNative(),
|
||||
getHandle()
|
||||
);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
|
||||
uint8_t val[] = {0x00, 0x00};
|
||||
BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902);
|
||||
desc->writeValue(val, 2);
|
||||
} // End Unregister
|
||||
|
||||
m_semaphoreRegForNotifyEvt.wait("registerForNotify");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< registerForNotify()");
|
||||
} // registerForNotify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Delete the descriptors in the descriptor map.
|
||||
* We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors
|
||||
* object references. Since we allocated these in this class, we are also responsible for deleteing
|
||||
* them. This method does just that.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLERemoteCharacteristic::removeDescriptors() {
|
||||
// Iterate through all the descriptors releasing their storage and erasing them from the map.
|
||||
for (auto &myPair : m_descriptorMap) {
|
||||
m_descriptorMap.erase(myPair.first);
|
||||
delete myPair.second;
|
||||
}
|
||||
m_descriptorMap.clear(); // Technically not neeeded, but just to be sure.
|
||||
} // removeCharacteristics
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a BLERemoteCharacteristic to a string representation;
|
||||
* @return a String representation.
|
||||
*/
|
||||
std::string BLERemoteCharacteristic::toString() {
|
||||
std::ostringstream ss;
|
||||
ss << "Characteristic: uuid: " << m_uuid.toString() <<
|
||||
", handle: " << getHandle() << " 0x" << std::hex << getHandle() <<
|
||||
", props: " << BLEUtils::characteristicPropertiesToString(m_charProp);
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write the new value for the characteristic.
|
||||
* @param [in] newValue The new value to write.
|
||||
* @param [in] response Do we expect a response?
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) {
|
||||
writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response);
|
||||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write the new value for the characteristic.
|
||||
*
|
||||
* This is a convenience function. Many BLE characteristics are a single byte of data.
|
||||
* @param [in] newValue The new byte value to write.
|
||||
* @param [in] response Whether we require a response from the write.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) {
|
||||
writeValue(&newValue, 1, response);
|
||||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write the new value for the characteristic from a data buffer.
|
||||
* @param [in] data A pointer to a data buffer.
|
||||
* @param [in] length The length of the data in the data buffer.
|
||||
* @param [in] response Whether we require a response from the write.
|
||||
*/
|
||||
void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) {
|
||||
// writeValue(std::string((char*)data, length), response);
|
||||
ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", length);
|
||||
|
||||
// Check to see that we are connected.
|
||||
if (!getRemoteService()->getClient()->isConnected()) {
|
||||
ESP_LOGE(LOG_TAG, "Disconnected");
|
||||
throw BLEDisconnectedException();
|
||||
}
|
||||
|
||||
m_semaphoreWriteCharEvt.take("writeValue");
|
||||
// Invoke the ESP-IDF API to perform the write.
|
||||
esp_err_t errRc = ::esp_ble_gattc_write_char(
|
||||
m_pRemoteService->getClient()->getGattcIf(),
|
||||
m_pRemoteService->getClient()->getConnId(),
|
||||
getHandle(),
|
||||
length,
|
||||
data,
|
||||
response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE
|
||||
);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreWriteCharEvt.wait("writeValue");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< writeValue");
|
||||
} // writeValue
|
||||
|
||||
/**
|
||||
* @brief Read raw data from remote characteristic as hex bytes
|
||||
* @return return pointer data read
|
||||
*/
|
||||
uint8_t* BLERemoteCharacteristic::readRawData() {
|
||||
return m_rawData;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
84
libraries/BLE/src/BLERemoteCharacteristic.h
Normal file
84
libraries/BLE/src/BLERemoteCharacteristic.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* BLERemoteCharacteristic.h
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
#include "BLERemoteService.h"
|
||||
#include "BLERemoteDescriptor.h"
|
||||
#include "BLEUUID.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLERemoteService;
|
||||
class BLERemoteDescriptor;
|
||||
typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
|
||||
|
||||
/**
|
||||
* @brief A model of a remote %BLE characteristic.
|
||||
*/
|
||||
class BLERemoteCharacteristic {
|
||||
public:
|
||||
~BLERemoteCharacteristic();
|
||||
|
||||
// Public member functions
|
||||
bool canBroadcast();
|
||||
bool canIndicate();
|
||||
bool canNotify();
|
||||
bool canRead();
|
||||
bool canWrite();
|
||||
bool canWriteNoResponse();
|
||||
BLERemoteDescriptor* getDescriptor(BLEUUID uuid);
|
||||
std::map<std::string, BLERemoteDescriptor*>* getDescriptors();
|
||||
uint16_t getHandle();
|
||||
BLEUUID getUUID();
|
||||
std::string readValue();
|
||||
uint8_t readUInt8();
|
||||
uint16_t readUInt16();
|
||||
uint32_t readUInt32();
|
||||
void registerForNotify(notify_callback _callback, bool notifications = true);
|
||||
void writeValue(uint8_t* data, size_t length, bool response = false);
|
||||
void writeValue(std::string newValue, bool response = false);
|
||||
void writeValue(uint8_t newValue, bool response = false);
|
||||
std::string toString();
|
||||
uint8_t* readRawData();
|
||||
|
||||
private:
|
||||
BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService);
|
||||
friend class BLEClient;
|
||||
friend class BLERemoteService;
|
||||
friend class BLERemoteDescriptor;
|
||||
|
||||
// Private member functions
|
||||
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam);
|
||||
|
||||
BLERemoteService* getRemoteService();
|
||||
void removeDescriptors();
|
||||
void retrieveDescriptors();
|
||||
|
||||
// Private properties
|
||||
BLEUUID m_uuid;
|
||||
esp_gatt_char_prop_t m_charProp;
|
||||
uint16_t m_handle;
|
||||
BLERemoteService* m_pRemoteService;
|
||||
FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt");
|
||||
std::string m_value;
|
||||
uint8_t *m_rawData;
|
||||
notify_callback m_notifyCallback;
|
||||
|
||||
// We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID.
|
||||
std::map<std::string, BLERemoteDescriptor*> m_descriptorMap;
|
||||
}; // BLERemoteCharacteristic
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */
|
181
libraries/BLE/src/BLERemoteDescriptor.cpp
Normal file
181
libraries/BLE/src/BLERemoteDescriptor.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* BLERemoteDescriptor.cpp
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include "BLERemoteDescriptor.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLERemoteDescriptor";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
BLERemoteDescriptor::BLERemoteDescriptor(
|
||||
uint16_t handle,
|
||||
BLEUUID uuid,
|
||||
BLERemoteCharacteristic* pRemoteCharacteristic) {
|
||||
|
||||
m_handle = handle;
|
||||
m_uuid = uuid;
|
||||
m_pRemoteCharacteristic = pRemoteCharacteristic;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the handle associated with this remote descriptor.
|
||||
* @return The handle associated with this remote descriptor.
|
||||
*/
|
||||
uint16_t BLERemoteDescriptor::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the characteristic that owns this descriptor.
|
||||
* @return The characteristic that owns this descriptor.
|
||||
*/
|
||||
BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() {
|
||||
return m_pRemoteCharacteristic;
|
||||
} // getRemoteCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the UUID associated this remote descriptor.
|
||||
* @return The UUID associated this remote descriptor.
|
||||
*/
|
||||
BLEUUID BLERemoteDescriptor::getUUID() {
|
||||
return m_uuid;
|
||||
} // getUUID
|
||||
|
||||
|
||||
std::string BLERemoteDescriptor::readValue() {
|
||||
ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str());
|
||||
|
||||
// Check to see that we are connected.
|
||||
if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
|
||||
ESP_LOGE(LOG_TAG, "Disconnected");
|
||||
throw BLEDisconnectedException();
|
||||
}
|
||||
|
||||
m_semaphoreReadDescrEvt.take("readValue");
|
||||
|
||||
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
|
||||
esp_err_t errRc = ::esp_ble_gattc_read_char_descr(
|
||||
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
|
||||
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server
|
||||
getHandle(), // The handle of this characteristic
|
||||
ESP_GATT_AUTH_REQ_NONE); // Security
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return "";
|
||||
}
|
||||
|
||||
// Block waiting for the event that indicates that the read has completed. When it has, the std::string found
|
||||
// in m_value will contain our data.
|
||||
m_semaphoreReadDescrEvt.wait("readValue");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
|
||||
return m_value;
|
||||
} // readValue
|
||||
|
||||
|
||||
uint8_t BLERemoteDescriptor::readUInt8() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 1) {
|
||||
return (uint8_t) value[0];
|
||||
}
|
||||
return 0;
|
||||
} // readUInt8
|
||||
|
||||
|
||||
uint16_t BLERemoteDescriptor::readUInt16() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 2) {
|
||||
return *(uint16_t*) value.data();
|
||||
}
|
||||
return 0;
|
||||
} // readUInt16
|
||||
|
||||
|
||||
uint32_t BLERemoteDescriptor::readUInt32() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 4) {
|
||||
return *(uint32_t*) value.data();
|
||||
}
|
||||
return 0;
|
||||
} // readUInt32
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of this BLE Remote Descriptor.
|
||||
* @retun A string representation of this BLE Remote Descriptor.
|
||||
*/
|
||||
std::string BLERemoteDescriptor::toString() {
|
||||
std::stringstream ss;
|
||||
ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString();
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data to the BLE Remote Descriptor.
|
||||
* @param [in] data The data to send to the remote descriptor.
|
||||
* @param [in] length The length of the data to send.
|
||||
* @param [in] response True if we expect a response.
|
||||
*/
|
||||
void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) {
|
||||
ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str());
|
||||
// Check to see that we are connected.
|
||||
if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
|
||||
ESP_LOGE(LOG_TAG, "Disconnected");
|
||||
throw BLEDisconnectedException();
|
||||
}
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gattc_write_char_descr(
|
||||
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
|
||||
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(),
|
||||
getHandle(),
|
||||
length, // Data length
|
||||
data, // Data
|
||||
response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE
|
||||
);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc);
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< writeValue");
|
||||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data represented as a string to the BLE Remote Descriptor.
|
||||
* @param [in] newValue The data to send to the remote descriptor.
|
||||
* @param [in] response True if we expect a response.
|
||||
*/
|
||||
void BLERemoteDescriptor::writeValue(std::string newValue, bool response) {
|
||||
writeValue((uint8_t*) newValue.data(), newValue.length(), response);
|
||||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write a byte value to the Descriptor.
|
||||
* @param [in] The single byte to write.
|
||||
* @param [in] True if we expect a response.
|
||||
*/
|
||||
void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) {
|
||||
writeValue(&newValue, 1, response);
|
||||
} // writeValue
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
55
libraries/BLE/src/BLERemoteDescriptor.h
Normal file
55
libraries/BLE/src/BLERemoteDescriptor.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* BLERemoteDescriptor.h
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
#include "BLERemoteCharacteristic.h"
|
||||
#include "BLEUUID.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLERemoteCharacteristic;
|
||||
/**
|
||||
* @brief A model of remote %BLE descriptor.
|
||||
*/
|
||||
class BLERemoteDescriptor {
|
||||
public:
|
||||
uint16_t getHandle();
|
||||
BLERemoteCharacteristic* getRemoteCharacteristic();
|
||||
BLEUUID getUUID();
|
||||
std::string readValue(void);
|
||||
uint8_t readUInt8(void);
|
||||
uint16_t readUInt16(void);
|
||||
uint32_t readUInt32(void);
|
||||
std::string toString(void);
|
||||
void writeValue(uint8_t* data, size_t length, bool response = false);
|
||||
void writeValue(std::string newValue, bool response = false);
|
||||
void writeValue(uint8_t newValue, bool response = false);
|
||||
|
||||
|
||||
private:
|
||||
friend class BLERemoteCharacteristic;
|
||||
BLERemoteDescriptor(
|
||||
uint16_t handle,
|
||||
BLEUUID uuid,
|
||||
BLERemoteCharacteristic* pRemoteCharacteristic
|
||||
);
|
||||
uint16_t m_handle; // Server handle of this descriptor.
|
||||
BLEUUID m_uuid; // UUID of this descriptor.
|
||||
std::string m_value; // Last received value of the descriptor.
|
||||
BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated.
|
||||
FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
|
||||
|
||||
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */
|
340
libraries/BLE/src/BLERemoteService.cpp
Normal file
340
libraries/BLE/src/BLERemoteService.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* BLERemoteService.cpp
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <sstream>
|
||||
#include "BLERemoteService.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include <esp_err.h>
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLERemoteService";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
BLERemoteService::BLERemoteService(
|
||||
esp_gatt_id_t srvcId,
|
||||
BLEClient* pClient,
|
||||
uint16_t startHandle,
|
||||
uint16_t endHandle
|
||||
) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, ">> BLERemoteService()");
|
||||
m_srvcId = srvcId;
|
||||
m_pClient = pClient;
|
||||
m_uuid = BLEUUID(m_srvcId);
|
||||
m_haveCharacteristics = false;
|
||||
m_startHandle = startHandle;
|
||||
m_endHandle = endHandle;
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< BLERemoteService()");
|
||||
}
|
||||
|
||||
|
||||
BLERemoteService::~BLERemoteService() {
|
||||
removeCharacteristics();
|
||||
}
|
||||
|
||||
/*
|
||||
static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
|
||||
if (id1.id.inst_id != id2.id.inst_id) {
|
||||
return false;
|
||||
}
|
||||
if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // compareSrvcId
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Handle GATT Client events
|
||||
*/
|
||||
void BLERemoteService::gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam) {
|
||||
switch (event) {
|
||||
//
|
||||
// ESP_GATTC_GET_CHAR_EVT
|
||||
//
|
||||
// get_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uin1t6_t conn_id
|
||||
// - esp_gatt_srvc_id_t srvc_id
|
||||
// - esp_gatt_id_t char_id
|
||||
// - esp_gatt_char_prop_t char_prop
|
||||
//
|
||||
/*
|
||||
case ESP_GATTC_GET_CHAR_EVT: {
|
||||
// Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be
|
||||
// the same.
|
||||
if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the status is NOT OK then we have a problem and continue.
|
||||
if (evtParam->get_char.status != ESP_GATT_OK) {
|
||||
m_semaphoreGetCharEvt.give();
|
||||
break;
|
||||
}
|
||||
|
||||
// This is an indication that we now have the characteristic details for a characteristic owned
|
||||
// by this service so remember it.
|
||||
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(
|
||||
BLEUUID(evtParam->get_char.char_id.uuid).toString(),
|
||||
new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) ));
|
||||
|
||||
|
||||
// Now that we have received a characteristic, lets ask for the next one.
|
||||
esp_err_t errRc = ::esp_ble_gattc_get_characteristic(
|
||||
m_pClient->getGattcIf(),
|
||||
m_pClient->getConnId(),
|
||||
&m_srvcId,
|
||||
&evtParam->get_char.char_id);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
break;
|
||||
}
|
||||
|
||||
//m_semaphoreGetCharEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_GET_CHAR_EVT
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
|
||||
// Send the event to each of the characteristics owned by this service.
|
||||
for (auto &myPair : m_characteristicMapByHandle) {
|
||||
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
|
||||
}
|
||||
} // gattClientEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the remote characteristic object for the characteristic UUID.
|
||||
* @param [in] uuid Remote characteristic uuid.
|
||||
* @return Reference to the remote characteristic object.
|
||||
* @throws BLEUuidNotFoundException
|
||||
*/
|
||||
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) {
|
||||
return getCharacteristic(BLEUUID(uuid));
|
||||
} // getCharacteristic
|
||||
|
||||
/**
|
||||
* @brief Get the characteristic object for the UUID.
|
||||
* @param [in] uuid Characteristic uuid.
|
||||
* @return Reference to the characteristic object.
|
||||
* @throws BLEUuidNotFoundException
|
||||
*/
|
||||
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) {
|
||||
// Design
|
||||
// ------
|
||||
// We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the
|
||||
// device what characteristics it has in which case we have nothing to match against. If we have not
|
||||
// asked the device about its characteristics, then we do that now. Once we get the results we can then
|
||||
// examine the characteristics map to see if it has the characteristic we are looking for.
|
||||
if (!m_haveCharacteristics) {
|
||||
retrieveCharacteristics();
|
||||
}
|
||||
std::string v = uuid.toString();
|
||||
for (auto &myPair : m_characteristicMap) {
|
||||
if (myPair.first == v) {
|
||||
return myPair.second;
|
||||
}
|
||||
}
|
||||
// throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another
|
||||
return nullptr;
|
||||
} // getCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve all the characteristics for this service.
|
||||
* This function will not return until we have all the characteristics.
|
||||
* @return N/A
|
||||
*/
|
||||
void BLERemoteService::retrieveCharacteristics() {
|
||||
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
|
||||
|
||||
removeCharacteristics(); // Forget any previous characteristics.
|
||||
|
||||
uint16_t offset = 0;
|
||||
esp_gattc_char_elem_t result;
|
||||
while (true) {
|
||||
uint16_t count = 10; // this value is used as in parameter that allows to search max 10 chars with the same uuid
|
||||
esp_gatt_status_t status = ::esp_ble_gattc_get_all_char(
|
||||
getClient()->getGattcIf(),
|
||||
getClient()->getConnId(),
|
||||
m_startHandle,
|
||||
m_endHandle,
|
||||
&result,
|
||||
&count,
|
||||
offset
|
||||
);
|
||||
|
||||
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != ESP_GATT_OK) { // If we got an error, end.
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0) { // If we failed to get any new records, end.
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
|
||||
|
||||
// We now have a new characteristic ... let us add that to our set of known characteristics
|
||||
BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(
|
||||
result.char_handle,
|
||||
BLEUUID(result.uuid),
|
||||
result.properties,
|
||||
this
|
||||
);
|
||||
|
||||
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
|
||||
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic*>(result.char_handle, pNewRemoteCharacteristic));
|
||||
offset++; // Increment our count of number of descriptors found.
|
||||
} // Loop forever (until we break inside the loop).
|
||||
|
||||
m_haveCharacteristics = true; // Remember that we have received the characteristics.
|
||||
ESP_LOGD(LOG_TAG, "<< getCharacteristics()");
|
||||
} // getCharacteristics
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve a map of all the characteristics of this service.
|
||||
* @return A map of all the characteristics of this service.
|
||||
*/
|
||||
std::map<std::string, BLERemoteCharacteristic*>* BLERemoteService::getCharacteristics() {
|
||||
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
|
||||
// If is possible that we have not read the characteristics associated with the service so do that
|
||||
// now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking
|
||||
// call and does not return until all the characteristics are available.
|
||||
if (!m_haveCharacteristics) {
|
||||
retrieveCharacteristics();
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str());
|
||||
return &m_characteristicMap;
|
||||
} // getCharacteristics
|
||||
|
||||
/**
|
||||
* @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID
|
||||
*/
|
||||
void BLERemoteService::getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap) {
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
|
||||
pCharacteristicMap = &m_characteristicMapByHandle;
|
||||
} // Get the characteristics map.
|
||||
|
||||
/**
|
||||
* @brief Get the client associated with this service.
|
||||
* @return A reference to the client associated with this service.
|
||||
*/
|
||||
BLEClient* BLERemoteService::getClient() {
|
||||
return m_pClient;
|
||||
} // getClient
|
||||
|
||||
|
||||
uint16_t BLERemoteService::getEndHandle() {
|
||||
return m_endHandle;
|
||||
} // getEndHandle
|
||||
|
||||
|
||||
esp_gatt_id_t* BLERemoteService::getSrvcId() {
|
||||
return &m_srvcId;
|
||||
} // getSrvcId
|
||||
|
||||
|
||||
uint16_t BLERemoteService::getStartHandle() {
|
||||
return m_startHandle;
|
||||
} // getStartHandle
|
||||
|
||||
|
||||
uint16_t BLERemoteService::getHandle() {
|
||||
ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str());
|
||||
ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle());
|
||||
return getStartHandle();
|
||||
} // getHandle
|
||||
|
||||
|
||||
BLEUUID BLERemoteService::getUUID() {
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the value of a characteristic associated with this service.
|
||||
*/
|
||||
std::string BLERemoteService::getValue(BLEUUID characteristicUuid) {
|
||||
ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
|
||||
std::string ret = getCharacteristic(characteristicUuid)->readValue();
|
||||
ESP_LOGD(LOG_TAG, "<< readValue");
|
||||
return ret;
|
||||
} // readValue
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Delete the characteristics in the characteristics map.
|
||||
* We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic
|
||||
* object references. Since we allocated these in this class, we are also responsible for deleteing
|
||||
* them. This method does just that.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLERemoteService::removeCharacteristics() {
|
||||
for (auto &myPair : m_characteristicMap) {
|
||||
delete myPair.second;
|
||||
//m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear
|
||||
}
|
||||
m_characteristicMap.clear(); // Clear the map
|
||||
for (auto &myPair : m_characteristicMapByHandle) {
|
||||
delete myPair.second;
|
||||
}
|
||||
m_characteristicMapByHandle.clear(); // Clear the map
|
||||
} // removeCharacteristics
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of a characteristic.
|
||||
* @param [in] characteristicUuid The characteristic to set.
|
||||
* @param [in] value The value to set.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) {
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
|
||||
getCharacteristic(characteristicUuid)->writeValue(value);
|
||||
ESP_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a string representation of this remote service.
|
||||
* @return A string representation of this remote service.
|
||||
*/
|
||||
std::string BLERemoteService::toString() {
|
||||
std::ostringstream ss;
|
||||
ss << "Service: uuid: " + m_uuid.toString();
|
||||
ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle <<
|
||||
", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle;
|
||||
for (auto &myPair : m_characteristicMap) {
|
||||
ss << "\n" << myPair.second->toString();
|
||||
// myPair.second is the value
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
85
libraries/BLE/src/BLERemoteService.h
Normal file
85
libraries/BLE/src/BLERemoteService.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* BLERemoteService.h
|
||||
*
|
||||
* Created on: Jul 8, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BLEClient.h"
|
||||
#include "BLERemoteCharacteristic.h"
|
||||
#include "BLEUUID.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEClient;
|
||||
class BLERemoteCharacteristic;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A model of a remote %BLE service.
|
||||
*/
|
||||
class BLERemoteService {
|
||||
public:
|
||||
virtual ~BLERemoteService();
|
||||
|
||||
// Public methods
|
||||
BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference.
|
||||
BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference.
|
||||
BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference.
|
||||
std::map<std::string, BLERemoteCharacteristic*>* getCharacteristics();
|
||||
std::map<uint16_t, BLERemoteCharacteristic*>* getCharacteristicsByHandle(); // Get the characteristics map.
|
||||
void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap);
|
||||
|
||||
BLEClient* getClient(void); // Get a reference to the client associated with this service.
|
||||
uint16_t getHandle(); // Get the handle of this service.
|
||||
BLEUUID getUUID(void); // Get the UUID of this service.
|
||||
std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic.
|
||||
void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic.
|
||||
std::string toString(void);
|
||||
|
||||
private:
|
||||
// Private constructor ... never meant to be created by a user application.
|
||||
BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle);
|
||||
|
||||
// Friends
|
||||
friend class BLEClient;
|
||||
friend class BLERemoteCharacteristic;
|
||||
|
||||
// Private methods
|
||||
void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server.
|
||||
esp_gatt_id_t* getSrvcId(void);
|
||||
uint16_t getStartHandle(); // Get the start handle for this service.
|
||||
uint16_t getEndHandle(); // Get the end handle for this service.
|
||||
|
||||
void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam);
|
||||
|
||||
void removeCharacteristics();
|
||||
|
||||
// Properties
|
||||
|
||||
// We maintain a map of characteristics owned by this service keyed by a string representation of the UUID.
|
||||
std::map<std::string, BLERemoteCharacteristic*> m_characteristicMap;
|
||||
|
||||
// We maintain a map of characteristics owned by this service keyed by a handle.
|
||||
std::map<uint16_t, BLERemoteCharacteristic*> m_characteristicMapByHandle;
|
||||
|
||||
bool m_haveCharacteristics; // Have we previously obtained the characteristics.
|
||||
BLEClient* m_pClient;
|
||||
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
|
||||
esp_gatt_id_t m_srvcId;
|
||||
BLEUUID m_uuid; // The UUID of this service.
|
||||
uint16_t m_startHandle; // The starting handle of this service.
|
||||
uint16_t m_endHandle; // The ending handle of this service.
|
||||
}; // BLERemoteService
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */
|
331
libraries/BLE/src/BLEScan.cpp
Normal file
331
libraries/BLE/src/BLEScan.cpp
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* BLEScan.cpp
|
||||
*
|
||||
* Created on: Jul 1, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEScan";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
BLEScan::BLEScan() {
|
||||
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan.
|
||||
m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
|
||||
m_pAdvertisedDeviceCallbacks = nullptr;
|
||||
m_stopped = true;
|
||||
m_wantDuplicates = false;
|
||||
setInterval(100);
|
||||
setWindow(100);
|
||||
} // BLEScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GAP events related to scans.
|
||||
* @param [in] event The event type for this event.
|
||||
* @param [in] param Parameter data for this event.
|
||||
*/
|
||||
void BLEScan::handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param) {
|
||||
|
||||
switch(event) {
|
||||
|
||||
// ---------------------------
|
||||
// scan_rst:
|
||||
// esp_gap_search_evt_t search_evt
|
||||
// esp_bd_addr_t bda
|
||||
// esp_bt_dev_type_t dev_type
|
||||
// esp_ble_addr_type_t ble_addr_type
|
||||
// esp_ble_evt_type_t ble_evt_type
|
||||
// int rssi
|
||||
// uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]
|
||||
// int flag
|
||||
// int num_resps
|
||||
// uint8_t adv_data_len
|
||||
// uint8_t scan_rsp_len
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
|
||||
switch(param->scan_rst.search_evt) {
|
||||
//
|
||||
// ESP_GAP_SEARCH_INQ_CMPL_EVT
|
||||
//
|
||||
// Event that indicates that the duration allowed for the search has completed or that we have been
|
||||
// asked to stop.
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT: {
|
||||
ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT");
|
||||
m_stopped = true;
|
||||
m_semaphoreScanEnd.give();
|
||||
if (m_scanCompleteCB != nullptr) {
|
||||
m_scanCompleteCB(m_scanResults);
|
||||
}
|
||||
break;
|
||||
} // ESP_GAP_SEARCH_INQ_CMPL_EVT
|
||||
|
||||
//
|
||||
// ESP_GAP_SEARCH_INQ_RES_EVT
|
||||
//
|
||||
// Result that has arrived back from a Scan inquiry.
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT: {
|
||||
if (m_stopped) { // If we are not scanning, nothing to do with the extra results.
|
||||
break;
|
||||
}
|
||||
|
||||
// Examine our list of previously scanned addresses and, if we found this one already,
|
||||
// ignore it.
|
||||
BLEAddress advertisedAddress(param->scan_rst.bda);
|
||||
bool found = false;
|
||||
|
||||
if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) {
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done.
|
||||
ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str());
|
||||
vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here
|
||||
break;
|
||||
}
|
||||
|
||||
// We now construct a model of the advertised device that we have just found for the first
|
||||
// time.
|
||||
// ESP_LOG_BUFFER_HEXDUMP(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG);
|
||||
// ESP_LOGW(LOG_TAG, "bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type);
|
||||
BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice();
|
||||
advertisedDevice->setAddress(advertisedAddress);
|
||||
advertisedDevice->setRSSI(param->scan_rst.rssi);
|
||||
advertisedDevice->setAdFlag(param->scan_rst.flag);
|
||||
advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len);
|
||||
advertisedDevice->setScan(this);
|
||||
advertisedDevice->setAddressType(param->scan_rst.ble_addr_type);
|
||||
|
||||
if (!found) { // If we have previously seen this device, don't record it again.
|
||||
m_scanResults.m_vectorAdvertisedDevices.insert(std::pair<std::string, BLEAdvertisedDevice*>(advertisedAddress.toString(), advertisedDevice));
|
||||
}
|
||||
|
||||
if (m_pAdvertisedDeviceCallbacks) {
|
||||
m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice);
|
||||
}
|
||||
if(found)
|
||||
delete advertisedDevice;
|
||||
|
||||
break;
|
||||
} // ESP_GAP_SEARCH_INQ_RES_EVT
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch - search_evt
|
||||
|
||||
|
||||
break;
|
||||
} // ESP_GAP_BLE_SCAN_RESULT_EVT
|
||||
|
||||
default: {
|
||||
break;
|
||||
} // default
|
||||
} // End switch
|
||||
} // gapEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Should we perform an active or passive scan?
|
||||
* The default is a passive scan. An active scan means that we will wish a scan response.
|
||||
* @param [in] active If true, we perform an active scan otherwise a passive scan.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEScan::setActiveScan(bool active) {
|
||||
if (active) {
|
||||
m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
|
||||
} else {
|
||||
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
|
||||
}
|
||||
} // setActiveScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the call backs to be invoked.
|
||||
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
|
||||
* @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false.
|
||||
*/
|
||||
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) {
|
||||
m_wantDuplicates = wantDuplicates;
|
||||
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
|
||||
} // setAdvertisedDeviceCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the interval to scan.
|
||||
* @param [in] The interval in msecs.
|
||||
*/
|
||||
void BLEScan::setInterval(uint16_t intervalMSecs) {
|
||||
m_scan_params.scan_interval = intervalMSecs / 0.625;
|
||||
} // setInterval
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the window to actively scan.
|
||||
* @param [in] windowMSecs How long to actively scan.
|
||||
*/
|
||||
void BLEScan::setWindow(uint16_t windowMSecs) {
|
||||
m_scan_params.scan_window = windowMSecs / 0.625;
|
||||
} // setWindow
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start scanning.
|
||||
* @param [in] duration The duration in seconds for which to scan.
|
||||
* @param [in] scanCompleteCB A function to be called when scanning has completed.
|
||||
* @param [in] are we continue scan (true) or we want to clear stored devices (false)
|
||||
* @return True if scan started or false if there was an error.
|
||||
*/
|
||||
bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) {
|
||||
ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
|
||||
|
||||
m_semaphoreScanEnd.take(std::string("start"));
|
||||
m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes.
|
||||
|
||||
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
|
||||
// then we should not clear map or we will connect the same device few times
|
||||
if(!is_continue) {
|
||||
for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
|
||||
delete _dev.second;
|
||||
}
|
||||
m_scanResults.m_vectorAdvertisedDevices.clear();
|
||||
}
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
m_semaphoreScanEnd.give();
|
||||
return false;
|
||||
}
|
||||
|
||||
errRc = ::esp_ble_gap_start_scanning(duration);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
m_semaphoreScanEnd.give();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_stopped = false;
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< start()");
|
||||
return true;
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start scanning and block until scanning has been completed.
|
||||
* @param [in] duration The duration in seconds for which to scan.
|
||||
* @return The BLEScanResults.
|
||||
*/
|
||||
BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) {
|
||||
if(start(duration, nullptr, is_continue)) {
|
||||
m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release.
|
||||
}
|
||||
return m_scanResults;
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stop an in progress scan.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEScan::stop() {
|
||||
ESP_LOGD(LOG_TAG, ">> stop()");
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gap_stop_scanning();
|
||||
|
||||
m_stopped = true;
|
||||
m_semaphoreScanEnd.give();
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< stop()");
|
||||
} // stop
|
||||
|
||||
// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
|
||||
void BLEScan::erase(BLEAddress address) {
|
||||
ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str());
|
||||
BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second;
|
||||
m_scanResults.m_vectorAdvertisedDevices.erase(address.toString());
|
||||
delete advertisedDevice;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Dump the scan results to the log.
|
||||
*/
|
||||
void BLEScanResults::dump() {
|
||||
ESP_LOGD(LOG_TAG, ">> Dump scan results:");
|
||||
for (int i=0; i<getCount(); i++) {
|
||||
ESP_LOGD(LOG_TAG, "- %s", getDevice(i).toString().c_str());
|
||||
}
|
||||
} // dump
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the count of devices found in the last scan.
|
||||
* @return The number of devices found in the last scan.
|
||||
*/
|
||||
int BLEScanResults::getCount() {
|
||||
return m_vectorAdvertisedDevices.size();
|
||||
} // getCount
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the specified device at the given index.
|
||||
* The index should be between 0 and getCount()-1.
|
||||
* @param [in] i The index of the device.
|
||||
* @return The device at the specified index.
|
||||
*/
|
||||
BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) {
|
||||
uint32_t x = 0;
|
||||
BLEAdvertisedDevice dev = *m_vectorAdvertisedDevices.begin()->second;
|
||||
for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) {
|
||||
dev = *it->second;
|
||||
if (x==i) break;
|
||||
x++;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
BLEScanResults BLEScan::getResults() {
|
||||
return m_scanResults;
|
||||
}
|
||||
|
||||
void BLEScan::clearResults() {
|
||||
for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
|
||||
delete _dev.second;
|
||||
}
|
||||
m_scanResults.m_vectorAdvertisedDevices.clear();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
83
libraries/BLE/src/BLEScan.h
Normal file
83
libraries/BLE/src/BLEScan.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* BLEScan.h
|
||||
*
|
||||
* Created on: Jul 1, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLESCAN_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h>
|
||||
|
||||
// #include <vector>
|
||||
#include <string>
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
#include "BLEClient.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEAdvertisedDevice;
|
||||
class BLEAdvertisedDeviceCallbacks;
|
||||
class BLEClient;
|
||||
class BLEScan;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The result of having performed a scan.
|
||||
* When a scan completes, we have a set of found devices. Each device is described
|
||||
* by a BLEAdvertisedDevice object. The number of items in the set is given by
|
||||
* getCount(). We can retrieve a device by calling getDevice() passing in the
|
||||
* index (starting at 0) of the desired device.
|
||||
*/
|
||||
class BLEScanResults {
|
||||
public:
|
||||
void dump();
|
||||
int getCount();
|
||||
BLEAdvertisedDevice getDevice(uint32_t i);
|
||||
|
||||
private:
|
||||
friend BLEScan;
|
||||
std::map<std::string, BLEAdvertisedDevice*> m_vectorAdvertisedDevices;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Perform and manage %BLE scans.
|
||||
*
|
||||
* Scanning is associated with a %BLE client that is attempting to locate BLE servers.
|
||||
*/
|
||||
class BLEScan {
|
||||
public:
|
||||
void setActiveScan(bool active);
|
||||
void setAdvertisedDeviceCallbacks(
|
||||
BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
|
||||
bool wantDuplicates = false);
|
||||
void setInterval(uint16_t intervalMSecs);
|
||||
void setWindow(uint16_t windowMSecs);
|
||||
bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);
|
||||
BLEScanResults start(uint32_t duration, bool is_continue = false);
|
||||
void stop();
|
||||
void erase(BLEAddress address);
|
||||
BLEScanResults getResults();
|
||||
void clearResults();
|
||||
|
||||
private:
|
||||
BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton.
|
||||
friend class BLEDevice;
|
||||
void handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload);
|
||||
|
||||
|
||||
esp_ble_scan_params_t m_scan_params;
|
||||
BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
|
||||
bool m_stopped = true;
|
||||
FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
|
||||
BLEScanResults m_scanResults;
|
||||
bool m_wantDuplicates;
|
||||
void (*m_scanCompleteCB)(BLEScanResults scanResults);
|
||||
}; // BLEScan
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */
|
104
libraries/BLE/src/BLESecurity.cpp
Normal file
104
libraries/BLE/src/BLESecurity.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* BLESecurity.cpp
|
||||
*
|
||||
* Created on: Dec 17, 2017
|
||||
* Author: chegewara
|
||||
*/
|
||||
|
||||
#include "BLESecurity.h"
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
BLESecurity::BLESecurity() {
|
||||
}
|
||||
|
||||
BLESecurity::~BLESecurity() {
|
||||
}
|
||||
/*
|
||||
* @brief Set requested authentication mode
|
||||
*/
|
||||
void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
|
||||
m_authReq = auth_req;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set our device IO capability to let end user perform authorization
|
||||
* either by displaying or entering generated 6-digits pin code
|
||||
*/
|
||||
void BLESecurity::setCapability(esp_ble_io_cap_t iocap) {
|
||||
m_iocap = iocap;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
} // setCapability
|
||||
|
||||
|
||||
/**
|
||||
* @brief Init encryption key by server
|
||||
* @param key_size is value between 7 and 16
|
||||
*/
|
||||
void BLESecurity::setInitEncryptionKey(uint8_t init_key) {
|
||||
m_initKey = init_key;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t));
|
||||
} // setInitEncryptionKey
|
||||
|
||||
|
||||
/**
|
||||
* @brief Init encryption key by client
|
||||
* @param key_size is value between 7 and 16
|
||||
*/
|
||||
void BLESecurity::setRespEncryptionKey(uint8_t resp_key) {
|
||||
m_respKey = resp_key;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t));
|
||||
} // setRespEncryptionKey
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void BLESecurity::setKeySize(uint8_t key_size) {
|
||||
m_keySize = key_size;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t));
|
||||
} //setKeySize
|
||||
|
||||
|
||||
/**
|
||||
* @brief Debug function to display what keys are exchanged by peers
|
||||
*/
|
||||
char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) {
|
||||
char* key_str = nullptr;
|
||||
switch (key_type) {
|
||||
case ESP_LE_KEY_NONE:
|
||||
key_str = (char*) "ESP_LE_KEY_NONE";
|
||||
break;
|
||||
case ESP_LE_KEY_PENC:
|
||||
key_str = (char*) "ESP_LE_KEY_PENC";
|
||||
break;
|
||||
case ESP_LE_KEY_PID:
|
||||
key_str = (char*) "ESP_LE_KEY_PID";
|
||||
break;
|
||||
case ESP_LE_KEY_PCSRK:
|
||||
key_str = (char*) "ESP_LE_KEY_PCSRK";
|
||||
break;
|
||||
case ESP_LE_KEY_PLK:
|
||||
key_str = (char*) "ESP_LE_KEY_PLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LLK:
|
||||
key_str = (char*) "ESP_LE_KEY_LLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LENC:
|
||||
key_str = (char*) "ESP_LE_KEY_LENC";
|
||||
break;
|
||||
case ESP_LE_KEY_LID:
|
||||
key_str = (char*) "ESP_LE_KEY_LID";
|
||||
break;
|
||||
case ESP_LE_KEY_LCSRK:
|
||||
key_str = (char*) "ESP_LE_KEY_LCSRK";
|
||||
break;
|
||||
default:
|
||||
key_str = (char*) "INVALID BLE KEY TYPE";
|
||||
break;
|
||||
}
|
||||
return key_str;
|
||||
} // esp_key_type_to_str
|
||||
#endif // CONFIG_BT_ENABLED
|
72
libraries/BLE/src/BLESecurity.h
Normal file
72
libraries/BLE/src/BLESecurity.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* BLESecurity.h
|
||||
*
|
||||
* Created on: Dec 17, 2017
|
||||
* Author: chegewara
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLESECURITY_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <esp_gap_ble_api.h>
|
||||
|
||||
class BLESecurity {
|
||||
public:
|
||||
BLESecurity();
|
||||
virtual ~BLESecurity();
|
||||
void setAuthenticationMode(esp_ble_auth_req_t auth_req);
|
||||
void setCapability(esp_ble_io_cap_t iocap);
|
||||
void setInitEncryptionKey(uint8_t init_key);
|
||||
void setRespEncryptionKey(uint8_t resp_key);
|
||||
void setKeySize(uint8_t key_size = 16);
|
||||
static char* esp_key_type_to_str(esp_ble_key_type_t key_type);
|
||||
|
||||
private:
|
||||
esp_ble_auth_req_t m_authReq;
|
||||
esp_ble_io_cap_t m_iocap;
|
||||
uint8_t m_initKey;
|
||||
uint8_t m_respKey;
|
||||
uint8_t m_keySize;
|
||||
|
||||
}; // BLESecurity
|
||||
|
||||
|
||||
/*
|
||||
* @brief Callbacks to handle GAP events related to authorization
|
||||
*/
|
||||
class BLESecurityCallbacks {
|
||||
public:
|
||||
virtual ~BLESecurityCallbacks() {};
|
||||
|
||||
/**
|
||||
* @brief Its request from peer device to input authentication pin code displayed on peer device.
|
||||
* It requires that our device is capable to input 6-digits code by end user
|
||||
* @return Return 6-digits integer value from input device
|
||||
*/
|
||||
virtual uint32_t onPassKeyRequest() = 0;
|
||||
|
||||
/**
|
||||
* @brief Provide us 6-digits code to perform authentication.
|
||||
* It requires that our device is capable to display this code to end user
|
||||
* @param
|
||||
*/
|
||||
virtual void onPassKeyNotify(uint32_t pass_key) = 0;
|
||||
|
||||
/**
|
||||
* @brief Here we can make decision if we want to let negotiate authorization with peer device or not
|
||||
* return Return true if we accept this peer device request
|
||||
*/
|
||||
|
||||
virtual bool onSecurityRequest() = 0 ;
|
||||
/**
|
||||
* Provide us information when authentication process is completed
|
||||
*/
|
||||
virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0;
|
||||
|
||||
virtual bool onConfirmPIN(uint32_t pin) = 0;
|
||||
}; // BLESecurityCallbacks
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_
|
424
libraries/BLE/src/BLEServer.cpp
Normal file
424
libraries/BLE/src/BLEServer.cpp
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* BLEServer.cpp
|
||||
*
|
||||
* Created on: Apr 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_bt.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include "GeneralUtils.h"
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEServer.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEUtils.h"
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEServer";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a %BLE Server
|
||||
*
|
||||
* This class is not designed to be individually instantiated. Instead one should create a server by asking
|
||||
* the BLEDevice class.
|
||||
*/
|
||||
BLEServer::BLEServer() {
|
||||
m_appId = ESP_GATT_IF_NONE;
|
||||
m_gatts_if = ESP_GATT_IF_NONE;
|
||||
m_connectedCount = 0;
|
||||
m_connId = ESP_GATT_IF_NONE;
|
||||
m_pServerCallbacks = nullptr;
|
||||
} // BLEServer
|
||||
|
||||
|
||||
void BLEServer::createApp(uint16_t appId) {
|
||||
m_appId = appId;
|
||||
registerApp(appId);
|
||||
} // createApp
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a %BLE Service.
|
||||
*
|
||||
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
|
||||
* of a new service. Every service must have a unique UUID.
|
||||
* @param [in] uuid The UUID of the new service.
|
||||
* @return A reference to the new service object.
|
||||
*/
|
||||
BLEService* BLEServer::createService(const char* uuid) {
|
||||
return createService(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a %BLE Service.
|
||||
*
|
||||
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
|
||||
* of a new service. Every service must have a unique UUID.
|
||||
* @param [in] uuid The UUID of the new service.
|
||||
* @param [in] numHandles The maximum number of handles associated with this service.
|
||||
* @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service.
|
||||
* @return A reference to the new service object.
|
||||
*/
|
||||
BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) {
|
||||
ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
|
||||
m_semaphoreCreateEvt.take("createService");
|
||||
|
||||
// Check that a service with the supplied UUID does not already exist.
|
||||
if (m_serviceMap.getByUUID(uuid) != nullptr) {
|
||||
ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.",
|
||||
uuid.toString().c_str());
|
||||
}
|
||||
|
||||
BLEService* pService = new BLEService(uuid, numHandles);
|
||||
pService->m_instId = inst_id;
|
||||
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
|
||||
pService->executeCreate(this); // Perform the API calls to actually create the service.
|
||||
|
||||
m_semaphoreCreateEvt.wait("createService");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< createService");
|
||||
return pService;
|
||||
} // createService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a %BLE Service by its UUID
|
||||
* @param [in] uuid The UUID of the new service.
|
||||
* @return A reference to the service object.
|
||||
*/
|
||||
BLEService* BLEServer::getServiceByUUID(const char* uuid) {
|
||||
return m_serviceMap.getByUUID(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a %BLE Service by its UUID
|
||||
* @param [in] uuid The UUID of the new service.
|
||||
* @return A reference to the service object.
|
||||
*/
|
||||
BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) {
|
||||
return m_serviceMap.getByUUID(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
||||
*
|
||||
* @return An advertising object.
|
||||
*/
|
||||
BLEAdvertising* BLEServer::getAdvertising() {
|
||||
return BLEDevice::getAdvertising();
|
||||
}
|
||||
|
||||
uint16_t BLEServer::getConnId() {
|
||||
return m_connId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the number of connected clients.
|
||||
* @return The number of connected clients.
|
||||
*/
|
||||
uint32_t BLEServer::getConnectedCount() {
|
||||
return m_connectedCount;
|
||||
} // getConnectedCount
|
||||
|
||||
|
||||
uint16_t BLEServer::getGattsIf() {
|
||||
return m_gatts_if;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle a GATT Server Event.
|
||||
*
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*
|
||||
*/
|
||||
void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
|
||||
ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s",
|
||||
BLEUtils::gattServerEventTypeToString(event).c_str());
|
||||
|
||||
switch(event) {
|
||||
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
|
||||
// add_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
//
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_EVT
|
||||
|
||||
case ESP_GATTS_MTU_EVT:
|
||||
updatePeerMTU(param->mtu.conn_id, param->mtu.mtu);
|
||||
break;
|
||||
|
||||
// ESP_GATTS_CONNECT_EVT
|
||||
// connect:
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
//
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
m_connId = param->connect.conn_id;
|
||||
addPeerDevice((void*)this, false, m_connId);
|
||||
if (m_pServerCallbacks != nullptr) {
|
||||
m_pServerCallbacks->onConnect(this);
|
||||
m_pServerCallbacks->onConnect(this, param);
|
||||
}
|
||||
m_connectedCount++; // Increment the number of connected devices count.
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_CREATE_EVT
|
||||
// Called when a new service is registered as having been created.
|
||||
//
|
||||
// create:
|
||||
// * esp_gatt_status_t status
|
||||
// * uint16_t service_handle
|
||||
// * esp_gatt_srvc_id_t service_id
|
||||
//
|
||||
case ESP_GATTS_CREATE_EVT: {
|
||||
BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid
|
||||
m_serviceMap.setByHandle(param->create.service_handle, pService);
|
||||
m_semaphoreCreateEvt.give();
|
||||
break;
|
||||
} // ESP_GATTS_CREATE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_DISCONNECT_EVT
|
||||
//
|
||||
// disconnect
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
// - esp_gatt_conn_reason_t reason
|
||||
//
|
||||
// If we receive a disconnect event then invoke the callback for disconnects (if one is present).
|
||||
// we also want to start advertising again.
|
||||
case ESP_GATTS_DISCONNECT_EVT: {
|
||||
m_connectedCount--; // Decrement the number of connected devices count.
|
||||
if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now.
|
||||
m_pServerCallbacks->onDisconnect(this);
|
||||
}
|
||||
startAdvertising(); //- do this with some delay from the loop()
|
||||
removePeerDevice(param->disconnect.conn_id, false);
|
||||
break;
|
||||
} // ESP_GATTS_DISCONNECT_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
|
||||
//
|
||||
// read:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool is_long
|
||||
// - bool need_rsp
|
||||
//
|
||||
case ESP_GATTS_READ_EVT: {
|
||||
break;
|
||||
} // ESP_GATTS_READ_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_REG_EVT
|
||||
// reg:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t app_id
|
||||
//
|
||||
case ESP_GATTS_REG_EVT: {
|
||||
m_gatts_if = gatts_if;
|
||||
m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app.
|
||||
break;
|
||||
} // ESP_GATTS_REG_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
|
||||
//
|
||||
// write:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool need_rsp
|
||||
// - bool is_prep
|
||||
// - uint16_t len
|
||||
// - uint8_t* value
|
||||
//
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTS_OPEN_EVT:
|
||||
m_semaphoreOpenEvt.give(param->open.status);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Invoke the handler for every Service we have.
|
||||
m_serviceMap.handleGATTServerEvent(event, gatts_if, param);
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register the app.
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
void BLEServer::registerApp(uint16_t m_appId) {
|
||||
ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId);
|
||||
m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event.
|
||||
::esp_ble_gatts_app_register(m_appId);
|
||||
m_semaphoreRegisterAppEvt.wait("registerApp");
|
||||
ESP_LOGD(LOG_TAG, "<< registerApp");
|
||||
} // registerApp
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the server callbacks.
|
||||
*
|
||||
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
|
||||
* disconnecting. This function can be called to register a callback handler that will be invoked when these
|
||||
* events are detected.
|
||||
*
|
||||
* @param [in] pCallbacks The callbacks to be invoked.
|
||||
*/
|
||||
void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) {
|
||||
m_pServerCallbacks = pCallbacks;
|
||||
} // setCallbacks
|
||||
|
||||
/*
|
||||
* Remove service
|
||||
*/
|
||||
void BLEServer::removeService(BLEService* service) {
|
||||
service->stop();
|
||||
service->executeDelete();
|
||||
m_serviceMap.removeService(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start advertising.
|
||||
*
|
||||
* Start the server advertising its existence. This is a convenience function and is equivalent to
|
||||
* retrieving the advertising object and invoking start upon it.
|
||||
*/
|
||||
void BLEServer::startAdvertising() {
|
||||
ESP_LOGD(LOG_TAG, ">> startAdvertising");
|
||||
BLEDevice::startAdvertising();
|
||||
ESP_LOGD(LOG_TAG, "<< startAdvertising");
|
||||
} // startAdvertising
|
||||
|
||||
/**
|
||||
* Allow to connect GATT server to peer device
|
||||
* Probably can be used in ANCS for iPhone
|
||||
*/
|
||||
bool BLEServer::connect(BLEAddress address) {
|
||||
esp_bd_addr_t addr;
|
||||
memcpy(&addr, address.getNative(), 6);
|
||||
// Perform the open connection request against the target BLE Server.
|
||||
m_semaphoreOpenEvt.take("connect");
|
||||
esp_err_t errRc = ::esp_ble_gatts_open(
|
||||
getGattsIf(),
|
||||
addr, // address
|
||||
1 // direct connection
|
||||
);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
|
||||
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
|
||||
return rc == ESP_GATT_OK;
|
||||
} // connect
|
||||
|
||||
|
||||
|
||||
void BLEServerCallbacks::onConnect(BLEServer* pServer) {
|
||||
ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default");
|
||||
ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
|
||||
ESP_LOGD("BLEServerCallbacks", "<< onConnect()");
|
||||
} // onConnect
|
||||
|
||||
void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) {
|
||||
ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default");
|
||||
ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
|
||||
ESP_LOGD("BLEServerCallbacks", "<< onConnect()");
|
||||
} // onConnect
|
||||
|
||||
|
||||
void BLEServerCallbacks::onDisconnect(BLEServer* pServer) {
|
||||
ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default");
|
||||
ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
|
||||
ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()");
|
||||
} // onDisconnect
|
||||
|
||||
/* multi connect support */
|
||||
/* TODO do some more tweaks */
|
||||
void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) {
|
||||
// set mtu in conn_status_t
|
||||
const std::map<uint16_t, conn_status_t>::iterator it = m_connectedServersMap.find(conn_id);
|
||||
if (it != m_connectedServersMap.end()) {
|
||||
it->second.mtu = mtu;
|
||||
std::swap(m_connectedServersMap[conn_id], it->second);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<uint16_t, conn_status_t> BLEServer::getPeerDevices(bool _client) {
|
||||
return m_connectedServersMap;
|
||||
}
|
||||
|
||||
|
||||
uint16_t BLEServer::getPeerMTU(uint16_t conn_id) {
|
||||
return m_connectedServersMap.find(conn_id)->second.mtu;
|
||||
}
|
||||
|
||||
void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
|
||||
conn_status_t status = {
|
||||
.peer_device = peer,
|
||||
.connected = true,
|
||||
.mtu = 23
|
||||
};
|
||||
|
||||
m_connectedServersMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
|
||||
}
|
||||
|
||||
void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
|
||||
m_connectedServersMap.erase(conn_id);
|
||||
}
|
||||
/* multi connect support */
|
||||
|
||||
/**
|
||||
* Update connection parameters can be called only after connection has been established
|
||||
*/
|
||||
void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) {
|
||||
esp_ble_conn_update_params_t conn_params;
|
||||
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t));
|
||||
conn_params.latency = latency;
|
||||
conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms
|
||||
conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms
|
||||
conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms
|
||||
esp_ble_gap_update_conn_params(&conn_params);
|
||||
}
|
||||
#endif // CONFIG_BT_ENABLED
|
140
libraries/BLE/src/BLEServer.h
Normal file
140
libraries/BLE/src/BLEServer.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* BLEServer.h
|
||||
*
|
||||
* Created on: Apr 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLESERVER_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gatts_api.h>
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
// #include "BLEDevice.h"
|
||||
|
||||
#include "BLEUUID.h"
|
||||
#include "BLEAdvertising.h"
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLESecurity.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "BLEAddress.h"
|
||||
|
||||
class BLEServerCallbacks;
|
||||
/* TODO possibly refactor this struct */
|
||||
typedef struct {
|
||||
void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here
|
||||
bool connected; // do we need it?
|
||||
uint16_t mtu; // every peer device negotiate own mtu
|
||||
} conn_status_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A data structure that manages the %BLE servers owned by a BLE server.
|
||||
*/
|
||||
class BLEServiceMap {
|
||||
public:
|
||||
BLEService* getByHandle(uint16_t handle);
|
||||
BLEService* getByUUID(const char* uuid);
|
||||
BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0);
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
|
||||
void setByHandle(uint16_t handle, BLEService* service);
|
||||
void setByUUID(const char* uuid, BLEService* service);
|
||||
void setByUUID(BLEUUID uuid, BLEService* service);
|
||||
std::string toString();
|
||||
BLEService* getFirst();
|
||||
BLEService* getNext();
|
||||
void removeService(BLEService *service);
|
||||
int getRegisteredServiceCount();
|
||||
|
||||
private:
|
||||
std::map<uint16_t, BLEService*> m_handleMap;
|
||||
std::map<BLEService*, std::string> m_uuidMap;
|
||||
std::map<BLEService*, std::string>::iterator m_iterator;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE server.
|
||||
*/
|
||||
class BLEServer {
|
||||
public:
|
||||
uint32_t getConnectedCount();
|
||||
BLEService* createService(const char* uuid);
|
||||
BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0);
|
||||
BLEAdvertising* getAdvertising();
|
||||
void setCallbacks(BLEServerCallbacks* pCallbacks);
|
||||
void startAdvertising();
|
||||
void removeService(BLEService* service);
|
||||
BLEService* getServiceByUUID(const char* uuid);
|
||||
BLEService* getServiceByUUID(BLEUUID uuid);
|
||||
bool connect(BLEAddress address);
|
||||
uint16_t m_appId;
|
||||
void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout);
|
||||
|
||||
/* multi connection support */
|
||||
std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
|
||||
void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
|
||||
void removePeerDevice(uint16_t conn_id, bool client);
|
||||
BLEServer* getServerByConnId(uint16_t conn_id);
|
||||
void updatePeerMTU(uint16_t connId, uint16_t mtu);
|
||||
uint16_t getPeerMTU(uint16_t conn_id);
|
||||
uint16_t getConnId();
|
||||
|
||||
|
||||
private:
|
||||
BLEServer();
|
||||
friend class BLEService;
|
||||
friend class BLECharacteristic;
|
||||
friend class BLEDevice;
|
||||
esp_ble_adv_data_t m_adv_data;
|
||||
// BLEAdvertising m_bleAdvertising;
|
||||
uint16_t m_connId;
|
||||
uint32_t m_connectedCount;
|
||||
uint16_t m_gatts_if;
|
||||
std::map<uint16_t, conn_status_t> m_connectedServersMap;
|
||||
|
||||
FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
|
||||
BLEServiceMap m_serviceMap;
|
||||
BLEServerCallbacks* m_pServerCallbacks = nullptr;
|
||||
|
||||
void createApp(uint16_t appId);
|
||||
uint16_t getGattsIf();
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
void registerApp(uint16_t);
|
||||
}; // BLEServer
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks associated with the operation of a %BLE server.
|
||||
*/
|
||||
class BLEServerCallbacks {
|
||||
public:
|
||||
virtual ~BLEServerCallbacks() {};
|
||||
/**
|
||||
* @brief Handle a new client connection.
|
||||
*
|
||||
* When a new client connects, we are invoked.
|
||||
*
|
||||
* @param [in] pServer A reference to the %BLE server that received the client connection.
|
||||
*/
|
||||
virtual void onConnect(BLEServer* pServer);
|
||||
virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param);
|
||||
/**
|
||||
* @brief Handle an existing client disconnection.
|
||||
*
|
||||
* When an existing client disconnects, we are invoked.
|
||||
*
|
||||
* @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
|
||||
*/
|
||||
virtual void onDisconnect(BLEServer* pServer);
|
||||
}; // BLEServerCallbacks
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */
|
418
libraries/BLE/src/BLEService.cpp
Normal file
418
libraries/BLE/src/BLEService.cpp
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* BLEService.cpp
|
||||
*
|
||||
* Created on: Mar 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
// A service is identified by a UUID. A service is also the container for one or more characteristics.
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_err.h>
|
||||
#include <esp_gatts_api.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "BLEServer.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEService"; // Tag for logging.
|
||||
#endif
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct an instance of the BLEService
|
||||
* @param [in] uuid The UUID of the service.
|
||||
* @param [in] numHandles The maximum number of handles associated with the service.
|
||||
*/
|
||||
BLEService::BLEService(const char* uuid, uint16_t numHandles) : BLEService(BLEUUID(uuid), numHandles) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct an instance of the BLEService
|
||||
* @param [in] uuid The UUID of the service.
|
||||
* @param [in] numHandles The maximum number of handles associated with the service.
|
||||
*/
|
||||
BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) {
|
||||
m_uuid = uuid;
|
||||
m_handle = NULL_HANDLE;
|
||||
m_pServer = nullptr;
|
||||
//m_serializeMutex.setName("BLEService");
|
||||
m_lastCreatedCharacteristic = nullptr;
|
||||
m_numHandles = numHandles;
|
||||
} // BLEService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create the service.
|
||||
* Create the service.
|
||||
* @param [in] gatts_if The handle of the GATT server interface.
|
||||
* @return N/A.
|
||||
*/
|
||||
|
||||
void BLEService::executeCreate(BLEServer* pServer) {
|
||||
ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str());
|
||||
m_pServer = pServer;
|
||||
m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT
|
||||
|
||||
esp_gatt_srvc_id_t srvc_id;
|
||||
srvc_id.is_primary = true;
|
||||
srvc_id.id.inst_id = m_instId;
|
||||
srvc_id.id.uuid = *m_uuid.getNative();
|
||||
esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service.
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreCreateEvt.wait("executeCreate");
|
||||
ESP_LOGD(LOG_TAG, "<< executeCreate");
|
||||
} // executeCreate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Delete the service.
|
||||
* Delete the service.
|
||||
* @return N/A.
|
||||
*/
|
||||
|
||||
void BLEService::executeDelete() {
|
||||
ESP_LOGD(LOG_TAG, ">> executeDelete()");
|
||||
m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle());
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreDeleteEvt.wait("executeDelete");
|
||||
ESP_LOGD(LOG_TAG, "<< executeDelete");
|
||||
} // executeDelete
|
||||
|
||||
|
||||
/**
|
||||
* @brief Dump details of this BLE GATT service.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEService::dump() {
|
||||
ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x",
|
||||
m_uuid.toString().c_str(),
|
||||
m_handle);
|
||||
ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str());
|
||||
} // dump
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the service.
|
||||
* @return the UUID of the service.
|
||||
*/
|
||||
BLEUUID BLEService::getUUID() {
|
||||
return m_uuid;
|
||||
} // getUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the service.
|
||||
* Here we wish to start the service which means that we will respond to partner requests about it.
|
||||
* Starting a service also means that we can create the corresponding characteristics.
|
||||
* @return Start the service.
|
||||
*/
|
||||
void BLEService::start() {
|
||||
// We ask the BLE runtime to start the service and then create each of the characteristics.
|
||||
// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event
|
||||
// obtained as a result of calling esp_ble_gatts_create_service().
|
||||
//
|
||||
ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str());
|
||||
if (m_handle == NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst();
|
||||
|
||||
while (pCharacteristic != nullptr) {
|
||||
m_lastCreatedCharacteristic = pCharacteristic;
|
||||
pCharacteristic->executeCreate(this);
|
||||
|
||||
pCharacteristic = m_characteristicMap.getNext();
|
||||
}
|
||||
// Start each of the characteristics ... these are found in the m_characteristicMap.
|
||||
|
||||
m_semaphoreStartEvt.take("start");
|
||||
esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
m_semaphoreStartEvt.wait("start");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< start()");
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stop the service.
|
||||
*/
|
||||
void BLEService::stop() {
|
||||
// We ask the BLE runtime to start the service and then create each of the characteristics.
|
||||
// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event
|
||||
// obtained as a result of calling esp_ble_gatts_create_service().
|
||||
ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str());
|
||||
if (m_handle == NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreStopEvt.take("stop");
|
||||
esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
m_semaphoreStopEvt.wait("stop");
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< stop()");
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the handle associated with this service.
|
||||
* @param [in] handle The handle associated with the service.
|
||||
*/
|
||||
void BLEService::setHandle(uint16_t handle) {
|
||||
ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str());
|
||||
if (m_handle != NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle);
|
||||
return;
|
||||
}
|
||||
m_handle = handle;
|
||||
ESP_LOGD(LOG_TAG, "<< setHandle");
|
||||
} // setHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the handle associated with this service.
|
||||
* @return The handle associated with this service.
|
||||
*/
|
||||
uint16_t BLEService::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a characteristic to the service.
|
||||
* @param [in] pCharacteristic A pointer to the characteristic to be added.
|
||||
*/
|
||||
void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) {
|
||||
// We maintain a mapping of characteristics owned by this service. These are managed by the
|
||||
// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic
|
||||
// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF).
|
||||
|
||||
ESP_LOGD(LOG_TAG, ">> addCharacteristic()");
|
||||
ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s",
|
||||
pCharacteristic->getUUID().toString().c_str(),
|
||||
toString().c_str());
|
||||
|
||||
// Check that we don't add the same characteristic twice.
|
||||
if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) {
|
||||
ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one");
|
||||
//return;
|
||||
}
|
||||
|
||||
// Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID
|
||||
// but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT.
|
||||
m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID());
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< addCharacteristic()");
|
||||
} // addCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new BLE Characteristic associated with this service.
|
||||
* @param [in] uuid - The UUID of the characteristic.
|
||||
* @param [in] properties - The properties of the characteristic.
|
||||
* @return The new BLE characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) {
|
||||
return createCharacteristic(BLEUUID(uuid), properties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new BLE Characteristic associated with this service.
|
||||
* @param [in] uuid - The UUID of the characteristic.
|
||||
* @param [in] properties - The properties of the characteristic.
|
||||
* @return The new BLE characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) {
|
||||
BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties);
|
||||
addCharacteristic(pCharacteristic);
|
||||
return pCharacteristic;
|
||||
} // createCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle a GATTS server event.
|
||||
*/
|
||||
void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
|
||||
switch (event) {
|
||||
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
|
||||
// add_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
|
||||
// If we have reached the correct service, then locate the characteristic and remember the handle
|
||||
// for that characteristic.
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
if (m_handle == param->add_char.service_handle) {
|
||||
BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic();
|
||||
if (pCharacteristic == nullptr) {
|
||||
ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!",
|
||||
BLEUUID(param->add_char.char_uuid).toString().c_str());
|
||||
dump();
|
||||
break;
|
||||
}
|
||||
pCharacteristic->setHandle(param->add_char.attr_handle);
|
||||
m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic);
|
||||
break;
|
||||
} // Reached the correct service.
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_START_EVT
|
||||
//
|
||||
// start:
|
||||
// esp_gatt_status_t status
|
||||
// uint16_t service_handle
|
||||
case ESP_GATTS_START_EVT: {
|
||||
if (param->start.service_handle == getHandle()) {
|
||||
m_semaphoreStartEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_START_EVT
|
||||
|
||||
// ESP_GATTS_STOP_EVT
|
||||
//
|
||||
// stop:
|
||||
// esp_gatt_status_t status
|
||||
// uint16_t service_handle
|
||||
//
|
||||
case ESP_GATTS_STOP_EVT: {
|
||||
if (param->stop.service_handle == getHandle()) {
|
||||
m_semaphoreStopEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_STOP_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_CREATE_EVT
|
||||
// Called when a new service is registered as having been created.
|
||||
//
|
||||
// create:
|
||||
// * esp_gatt_status_t status
|
||||
// * uint16_t service_handle
|
||||
// * esp_gatt_srvc_id_t service_id
|
||||
// * - esp_gatt_id id
|
||||
// * - esp_bt_uuid uuid
|
||||
// * - uint8_t inst_id
|
||||
// * - bool is_primary
|
||||
//
|
||||
case ESP_GATTS_CREATE_EVT: {
|
||||
if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) {
|
||||
setHandle(param->create.service_handle);
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_CREATE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_DELETE_EVT
|
||||
// Called when a service is deleted.
|
||||
//
|
||||
// delete:
|
||||
// * esp_gatt_status_t status
|
||||
// * uint16_t service_handle
|
||||
//
|
||||
case ESP_GATTS_DELETE_EVT: {
|
||||
if (param->del.service_handle == getHandle()) {
|
||||
m_semaphoreDeleteEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_DELETE_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
} // Switch
|
||||
|
||||
// Invoke the GATTS handler in each of the associated characteristics.
|
||||
m_characteristicMap.handleGATTServerEvent(event, gatts_if, param);
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
BLECharacteristic* BLEService::getCharacteristic(const char* uuid) {
|
||||
return getCharacteristic(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) {
|
||||
return m_characteristicMap.getByUUID(uuid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of this service.
|
||||
* A service is defined by:
|
||||
* * Its UUID
|
||||
* * Its handle
|
||||
* @return A string representation of this service.
|
||||
*/
|
||||
std::string BLEService::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << "UUID: " << getUUID().toString() <<
|
||||
", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle();
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the last created characteristic.
|
||||
* It is lamentable that this function has to exist. It returns the last created characteristic.
|
||||
* We need this because the descriptor API is built around the notion that a new descriptor, when created,
|
||||
* is associated with the last characteristics created and we need that information.
|
||||
* @return The last created characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLEService::getLastCreatedCharacteristic() {
|
||||
return m_lastCreatedCharacteristic;
|
||||
} // getLastCreatedCharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the BLE server associated with this service.
|
||||
* @return The BLEServer associated with this service.
|
||||
*/
|
||||
BLEServer* BLEService::getServer() {
|
||||
return m_pServer;
|
||||
} // getServer
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
97
libraries/BLE/src/BLEService.h
Normal file
97
libraries/BLE/src/BLEService.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* BLEService.h
|
||||
*
|
||||
* Created on: Mar 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLESERVICE_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <esp_gatts_api.h>
|
||||
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEServer.h"
|
||||
#include "BLEUUID.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEServer;
|
||||
|
||||
/**
|
||||
* @brief A data mapping used to manage the set of %BLE characteristics known to the server.
|
||||
*/
|
||||
class BLECharacteristicMap {
|
||||
public:
|
||||
void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid);
|
||||
void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid);
|
||||
void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic);
|
||||
BLECharacteristic* getByUUID(const char* uuid);
|
||||
BLECharacteristic* getByUUID(BLEUUID uuid);
|
||||
BLECharacteristic* getByHandle(uint16_t handle);
|
||||
BLECharacteristic* getFirst();
|
||||
BLECharacteristic* getNext();
|
||||
std::string toString();
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
private:
|
||||
std::map<BLECharacteristic*, std::string> m_uuidMap;
|
||||
std::map<uint16_t, BLECharacteristic*> m_handleMap;
|
||||
std::map<BLECharacteristic*, std::string>::iterator m_iterator;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE service.
|
||||
*
|
||||
*/
|
||||
class BLEService {
|
||||
public:
|
||||
void addCharacteristic(BLECharacteristic* pCharacteristic);
|
||||
BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties);
|
||||
BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties);
|
||||
void dump();
|
||||
void executeCreate(BLEServer* pServer);
|
||||
void executeDelete();
|
||||
BLECharacteristic* getCharacteristic(const char* uuid);
|
||||
BLECharacteristic* getCharacteristic(BLEUUID uuid);
|
||||
BLEUUID getUUID();
|
||||
BLEServer* getServer();
|
||||
void start();
|
||||
void stop();
|
||||
std::string toString();
|
||||
uint16_t getHandle();
|
||||
uint8_t m_instId = 0;
|
||||
|
||||
private:
|
||||
BLEService(const char* uuid, uint16_t numHandles);
|
||||
BLEService(BLEUUID uuid, uint16_t numHandles);
|
||||
friend class BLEServer;
|
||||
friend class BLEServiceMap;
|
||||
friend class BLEDescriptor;
|
||||
friend class BLECharacteristic;
|
||||
friend class BLEDevice;
|
||||
|
||||
BLECharacteristicMap m_characteristicMap;
|
||||
uint16_t m_handle;
|
||||
BLECharacteristic* m_lastCreatedCharacteristic = nullptr;
|
||||
BLEServer* m_pServer = nullptr;
|
||||
BLEUUID m_uuid;
|
||||
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt");
|
||||
|
||||
uint16_t m_numHandles;
|
||||
|
||||
BLECharacteristic* getLastCreatedCharacteristic();
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
|
||||
void setHandle(uint16_t handle);
|
||||
//void setService(esp_gatt_srvc_id_t srvc_id);
|
||||
}; // BLEService
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */
|
134
libraries/BLE/src/BLEServiceMap.cpp
Normal file
134
libraries/BLE/src/BLEServiceMap.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* BLEServiceMap.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "BLEService.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the service by UUID.
|
||||
* @param [in] UUID The UUID to look up the service.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLEService* BLEServiceMap::getByUUID(const char* uuid) {
|
||||
return getByUUID(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the service by UUID.
|
||||
* @param [in] UUID The UUID to look up the service.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLEService* BLEServiceMap::getByUUID(BLEUUID uuid, uint8_t inst_id) {
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (myPair.first->getUUID().equals(uuid)) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
//return m_uuidMap.at(uuid.toString());
|
||||
return nullptr;
|
||||
} // getByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the service by handle.
|
||||
* @param [in] handle The handle to look up the service.
|
||||
* @return The service.
|
||||
*/
|
||||
BLEService* BLEServiceMap::getByHandle(uint16_t handle) {
|
||||
return m_handleMap.at(handle);
|
||||
} // getByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the service by UUID.
|
||||
* @param [in] uuid The uuid of the service.
|
||||
* @param [in] characteristic The service to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService* service) {
|
||||
m_uuidMap.insert(std::pair<BLEService*, std::string>(service, uuid.toString()));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the service by handle.
|
||||
* @param [in] handle The handle of the service.
|
||||
* @param [in] service The service to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) {
|
||||
m_handleMap.insert(std::pair<uint16_t, BLEService*>(handle, service));
|
||||
} // setByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the service map.
|
||||
* @return A string representation of the service map.
|
||||
*/
|
||||
std::string BLEServiceMap::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << std::hex << std::setfill('0');
|
||||
for (auto &myPair: m_handleMap) {
|
||||
stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n";
|
||||
}
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
void BLEServiceMap::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
// Invoke the handler for every Service we have.
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
myPair.first->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the first service in the map.
|
||||
* @return The first service in the map.
|
||||
*/
|
||||
BLEService* BLEServiceMap::getFirst() {
|
||||
m_iterator = m_uuidMap.begin();
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEService* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getFirst
|
||||
|
||||
/**
|
||||
* @brief Get the next service in the map.
|
||||
* @return The next service in the map.
|
||||
*/
|
||||
BLEService* BLEServiceMap::getNext() {
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEService* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getNext
|
||||
|
||||
/**
|
||||
* @brief Removes service from maps.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEServiceMap::removeService(BLEService* service) {
|
||||
m_handleMap.erase(service->getHandle());
|
||||
m_uuidMap.erase(service);
|
||||
} // removeService
|
||||
|
||||
/**
|
||||
* @brief Returns the amount of registered services
|
||||
* @return amount of registered services
|
||||
*/
|
||||
int BLEServiceMap::getRegisteredServiceCount(){
|
||||
return m_handleMap.size();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
407
libraries/BLE/src/BLEUUID.cpp
Normal file
407
libraries/BLE/src/BLEUUID.cpp
Normal file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* BLEUUID.cpp
|
||||
*
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "BLEUUID.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEUUID";
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy memory from source to target but in reverse order.
|
||||
*
|
||||
* When we move memory from one location it is normally:
|
||||
*
|
||||
* ```
|
||||
* [0][1][2]...[n] -> [0][1][2]...[n]
|
||||
* ```
|
||||
*
|
||||
* with this function, it is:
|
||||
*
|
||||
* ```
|
||||
* [0][1][2]...[n] -> [n][n-1][n-2]...[0]
|
||||
* ```
|
||||
*
|
||||
* @param [in] target The target of the copy
|
||||
* @param [in] source The source of the copy
|
||||
* @param [in] size The number of bytes to copy
|
||||
*/
|
||||
static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) {
|
||||
assert(size > 0);
|
||||
target += (size - 1); // Point target to the last byte of the target data
|
||||
while (size > 0) {
|
||||
*target = *source;
|
||||
target--;
|
||||
source++;
|
||||
size--;
|
||||
}
|
||||
} // memrcpy
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from a string.
|
||||
*
|
||||
* Create a UUID from a string. There will be two possible stories here. Either the string represents
|
||||
* a binary data field or the string represents a hex encoding of a UUID.
|
||||
* For the hex encoding, here is an example:
|
||||
*
|
||||
* ```
|
||||
* "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* 12345678-90ab-cdef-1234-567890abcdef
|
||||
* ```
|
||||
*
|
||||
* This has a length of 36 characters. We need to parse this into 16 bytes.
|
||||
*
|
||||
* @param [in] value The string to build a UUID from.
|
||||
*/
|
||||
BLEUUID::BLEUUID(std::string value) {
|
||||
m_valueSet = true;
|
||||
if (value.length() == 4) {
|
||||
m_uuid.len = ESP_UUID_LEN_16;
|
||||
m_uuid.uuid.uuid16 = 0;
|
||||
for(int i=0;i<value.length();){
|
||||
uint8_t MSB = value.c_str()[i];
|
||||
uint8_t LSB = value.c_str()[i+1];
|
||||
|
||||
if(MSB > '9') MSB -= 7;
|
||||
if(LSB > '9') LSB -= 7;
|
||||
m_uuid.uuid.uuid16 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4;
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
else if (value.length() == 8) {
|
||||
m_uuid.len = ESP_UUID_LEN_32;
|
||||
m_uuid.uuid.uuid32 = 0;
|
||||
for(int i=0;i<value.length();){
|
||||
uint8_t MSB = value.c_str()[i];
|
||||
uint8_t LSB = value.c_str()[i+1];
|
||||
|
||||
if(MSB > '9') MSB -= 7;
|
||||
if(LSB > '9') LSB -= 7;
|
||||
m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4;
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time)
|
||||
m_uuid.len = ESP_UUID_LEN_128;
|
||||
memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16);
|
||||
}
|
||||
else if (value.length() == 36) {
|
||||
// If the length of the string is 36 bytes then we will assume it is a long hex string in
|
||||
// UUID format.
|
||||
m_uuid.len = ESP_UUID_LEN_128;
|
||||
int n = 0;
|
||||
for(int i=0;i<value.length();){
|
||||
if(value.c_str()[i] == '-')
|
||||
i++;
|
||||
uint8_t MSB = value.c_str()[i];
|
||||
uint8_t LSB = value.c_str()[i+1];
|
||||
|
||||
if(MSB > '9') MSB -= 7;
|
||||
if(LSB > '9') LSB -= 7;
|
||||
m_uuid.uuid.uuid128[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F);
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes");
|
||||
m_valueSet = false;
|
||||
}
|
||||
} //BLEUUID(std::string)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from 16 bytes of memory.
|
||||
*
|
||||
* @param [in] pData The pointer to the start of the UUID.
|
||||
* @param [in] size The size of the data.
|
||||
* @param [in] msbFirst Is the MSB first in pData memory?
|
||||
*/
|
||||
BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) {
|
||||
if (size != 16) {
|
||||
ESP_LOGE(LOG_TAG, "ERROR: UUID length not 16 bytes");
|
||||
return;
|
||||
}
|
||||
m_uuid.len = ESP_UUID_LEN_128;
|
||||
if (msbFirst) {
|
||||
memrcpy(m_uuid.uuid.uuid128, pData, 16);
|
||||
} else {
|
||||
memcpy(m_uuid.uuid.uuid128, pData, 16);
|
||||
}
|
||||
m_valueSet = true;
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from the 16bit value.
|
||||
*
|
||||
* @param [in] uuid The 16bit short form UUID.
|
||||
*/
|
||||
BLEUUID::BLEUUID(uint16_t uuid) {
|
||||
m_uuid.len = ESP_UUID_LEN_16;
|
||||
m_uuid.uuid.uuid16 = uuid;
|
||||
m_valueSet = true;
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from the 32bit value.
|
||||
*
|
||||
* @param [in] uuid The 32bit short form UUID.
|
||||
*/
|
||||
BLEUUID::BLEUUID(uint32_t uuid) {
|
||||
m_uuid.len = ESP_UUID_LEN_32;
|
||||
m_uuid.uuid.uuid32 = uuid;
|
||||
m_valueSet = true;
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from the native UUID.
|
||||
*
|
||||
* @param [in] uuid The native UUID.
|
||||
*/
|
||||
BLEUUID::BLEUUID(esp_bt_uuid_t uuid) {
|
||||
m_uuid = uuid;
|
||||
m_valueSet = true;
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a UUID from the ESP32 esp_gat_id_t.
|
||||
*
|
||||
* @param [in] gattId The data to create the UUID from.
|
||||
*/
|
||||
BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) {
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
BLEUUID::BLEUUID() {
|
||||
m_valueSet = false;
|
||||
} // BLEUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the number of bits in this uuid.
|
||||
* @return The number of bits in the UUID. One of 16, 32 or 128.
|
||||
*/
|
||||
uint8_t BLEUUID::bitSize() {
|
||||
if (!m_valueSet) return 0;
|
||||
switch (m_uuid.len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
return 16;
|
||||
case ESP_UUID_LEN_32:
|
||||
return 32;
|
||||
case ESP_UUID_LEN_128:
|
||||
return 128;
|
||||
default:
|
||||
ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len);
|
||||
return 0;
|
||||
} // End of switch
|
||||
} // bitSize
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compare a UUID against this UUID.
|
||||
*
|
||||
* @param [in] uuid The UUID to compare against.
|
||||
* @return True if the UUIDs are equal and false otherwise.
|
||||
*/
|
||||
bool BLEUUID::equals(BLEUUID uuid) {
|
||||
//ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str());
|
||||
if (!m_valueSet || !uuid.m_valueSet) return false;
|
||||
|
||||
if (uuid.m_uuid.len != m_uuid.len) {
|
||||
return uuid.toString() == toString();
|
||||
}
|
||||
|
||||
if (uuid.m_uuid.len == ESP_UUID_LEN_16) {
|
||||
return uuid.m_uuid.uuid.uuid16 == m_uuid.uuid.uuid16;
|
||||
}
|
||||
|
||||
if (uuid.m_uuid.len == ESP_UUID_LEN_32) {
|
||||
return uuid.m_uuid.uuid.uuid32 == m_uuid.uuid.uuid32;
|
||||
}
|
||||
|
||||
return memcmp(uuid.m_uuid.uuid.uuid128, m_uuid.uuid.uuid128, 16) == 0;
|
||||
} // equals
|
||||
|
||||
|
||||
/**
|
||||
* Create a BLEUUID from a string of the form:
|
||||
* 0xNNNN
|
||||
* 0xNNNNNNNN
|
||||
* 0x<UUID>
|
||||
* NNNN
|
||||
* NNNNNNNN
|
||||
* <UUID>
|
||||
*/
|
||||
BLEUUID BLEUUID::fromString(std::string _uuid) {
|
||||
uint8_t start = 0;
|
||||
if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
|
||||
start = 2;
|
||||
}
|
||||
uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use.
|
||||
|
||||
if(len == 4) {
|
||||
uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
|
||||
return BLEUUID(x);
|
||||
} else if (len == 8) {
|
||||
uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
|
||||
return BLEUUID(x);
|
||||
} else if (len == 36) {
|
||||
return BLEUUID(_uuid);
|
||||
}
|
||||
return BLEUUID();
|
||||
} // fromString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the native UUID value.
|
||||
*
|
||||
* @return The native UUID value or NULL if not set.
|
||||
*/
|
||||
esp_bt_uuid_t* BLEUUID::getNative() {
|
||||
//ESP_LOGD(TAG, ">> getNative()")
|
||||
if (m_valueSet == false) {
|
||||
ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!");
|
||||
return nullptr;
|
||||
}
|
||||
//ESP_LOGD(TAG, "<< getNative()");
|
||||
return &m_uuid;
|
||||
} // getNative
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a UUID to its 128 bit representation.
|
||||
*
|
||||
* A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
|
||||
* will convert 16 or 32 bit representations to the full 128bit.
|
||||
*/
|
||||
BLEUUID BLEUUID::to128() {
|
||||
//ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str());
|
||||
|
||||
// If we either don't have a value or are already a 128 bit UUID, nothing further to do.
|
||||
if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID.
|
||||
if (m_uuid.len == ESP_UUID_LEN_16) {
|
||||
uint16_t temp = m_uuid.uuid.uuid16;
|
||||
m_uuid.uuid.uuid128[15] = 0;
|
||||
m_uuid.uuid.uuid128[14] = 0;
|
||||
m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff;
|
||||
m_uuid.uuid.uuid128[12] = temp & 0xff;
|
||||
|
||||
}
|
||||
else if (m_uuid.len == ESP_UUID_LEN_32) {
|
||||
uint32_t temp = m_uuid.uuid.uuid32;
|
||||
m_uuid.uuid.uuid128[15] = (temp >> 24) & 0xff;
|
||||
m_uuid.uuid.uuid128[14] = (temp >> 16) & 0xff;
|
||||
m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff;
|
||||
m_uuid.uuid.uuid128[12] = temp & 0xff;
|
||||
}
|
||||
|
||||
// Set the fixed parts of the UUID.
|
||||
m_uuid.uuid.uuid128[11] = 0x00;
|
||||
m_uuid.uuid.uuid128[10] = 0x00;
|
||||
|
||||
m_uuid.uuid.uuid128[9] = 0x10;
|
||||
m_uuid.uuid.uuid128[8] = 0x00;
|
||||
|
||||
m_uuid.uuid.uuid128[7] = 0x80;
|
||||
m_uuid.uuid.uuid128[6] = 0x00;
|
||||
|
||||
m_uuid.uuid.uuid128[5] = 0x00;
|
||||
m_uuid.uuid.uuid128[4] = 0x80;
|
||||
m_uuid.uuid.uuid128[3] = 0x5f;
|
||||
m_uuid.uuid.uuid128[2] = 0x9b;
|
||||
m_uuid.uuid.uuid128[1] = 0x34;
|
||||
m_uuid.uuid.uuid128[0] = 0xfb;
|
||||
|
||||
m_uuid.len = ESP_UUID_LEN_128;
|
||||
//ESP_LOGD(TAG, "<< toFull <- %s", toString().c_str());
|
||||
return *this;
|
||||
} // to128
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a string representation of the UUID.
|
||||
*
|
||||
* The format of a string is:
|
||||
* 01234567 8901 2345 6789 012345678901
|
||||
* 0000180d-0000-1000-8000-00805f9b34fb
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
*
|
||||
* @return A string representation of the UUID.
|
||||
*/
|
||||
std::string BLEUUID::toString() {
|
||||
if (!m_valueSet) return "<NULL>"; // If we have no value, nothing to format.
|
||||
|
||||
// If the UUIDs are 16 or 32 bit, pad correctly.
|
||||
std::stringstream ss;
|
||||
|
||||
if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly.
|
||||
ss << "0000" <<
|
||||
std::hex <<
|
||||
std::setfill('0') <<
|
||||
std::setw(4) <<
|
||||
m_uuid.uuid.uuid16 <<
|
||||
"-0000-1000-8000-00805f9b34fb";
|
||||
return ss.str(); // Return the string
|
||||
} // End 16bit UUID
|
||||
|
||||
if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly.
|
||||
ss << std::hex <<
|
||||
std::setfill('0') <<
|
||||
std::setw(8) <<
|
||||
m_uuid.uuid.uuid32 <<
|
||||
"-0000-1000-8000-00805f9b34fb";
|
||||
return ss.str(); // return the string
|
||||
} // End 32bit UUID
|
||||
|
||||
// The UUID is not 16bit or 32bit which means that it is 128bit.
|
||||
//
|
||||
// UUID string format:
|
||||
// AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP
|
||||
ss << std::hex << std::setfill('0') <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[15] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[14] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[13] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[12] << "-" <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[11] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[10] << "-" <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[9] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[8] << "-" <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[7] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[6] << "-" <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[5] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[4] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[3] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[2] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[1] <<
|
||||
std::setw(2) << (int) m_uuid.uuid.uuid128[0];
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
39
libraries/BLE/src/BLEUUID.h
Normal file
39
libraries/BLE/src/BLEUUID.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* BLEUUID.h
|
||||
*
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEUUID_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gatt_defs.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE UUID.
|
||||
*/
|
||||
class BLEUUID {
|
||||
public:
|
||||
BLEUUID(std::string uuid);
|
||||
BLEUUID(uint16_t uuid);
|
||||
BLEUUID(uint32_t uuid);
|
||||
BLEUUID(esp_bt_uuid_t uuid);
|
||||
BLEUUID(uint8_t* pData, size_t size, bool msbFirst);
|
||||
BLEUUID(esp_gatt_id_t gattId);
|
||||
BLEUUID();
|
||||
uint8_t bitSize(); // Get the number of bits in this uuid.
|
||||
bool equals(BLEUUID uuid);
|
||||
esp_bt_uuid_t* getNative();
|
||||
BLEUUID to128();
|
||||
std::string toString();
|
||||
static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string
|
||||
|
||||
private:
|
||||
esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps.
|
||||
bool m_valueSet = false; // Is there a value set for this instance.
|
||||
}; // BLEUUID
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */
|
2033
libraries/BLE/src/BLEUtils.cpp
Normal file
2033
libraries/BLE/src/BLEUtils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
63
libraries/BLE/src/BLEUtils.h
Normal file
63
libraries/BLE/src/BLEUtils.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* BLEUtils.h
|
||||
*
|
||||
* Created on: Mar 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEUTILS_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gattc_api.h> // ESP32 BLE
|
||||
#include <esp_gatts_api.h> // ESP32 BLE
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <string>
|
||||
#include "BLEClient.h"
|
||||
|
||||
/**
|
||||
* @brief A set of general %BLE utilities.
|
||||
*/
|
||||
class BLEUtils {
|
||||
public:
|
||||
static const char* addressTypeToString(esp_ble_addr_type_t type);
|
||||
static std::string adFlagsToString(uint8_t adFlags);
|
||||
static const char* advTypeToString(uint8_t advType);
|
||||
static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length);
|
||||
static std::string buildPrintData(uint8_t* source, size_t length);
|
||||
static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop);
|
||||
static const char* devTypeToString(esp_bt_dev_type_t type);
|
||||
static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id = 0);
|
||||
static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true);
|
||||
static void dumpGapEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
static void dumpGattClientEvent(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam);
|
||||
static void dumpGattServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* evtParam);
|
||||
static const char* eventTypeToString(esp_ble_evt_type_t eventType);
|
||||
static BLEClient* findByAddress(BLEAddress address);
|
||||
static BLEClient* findByConnId(uint16_t conn_id);
|
||||
static const char* gapEventToString(uint32_t eventType);
|
||||
static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID);
|
||||
static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType);
|
||||
static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason);
|
||||
static std::string gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement);
|
||||
static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID);
|
||||
static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType);
|
||||
static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId);
|
||||
static std::string gattServiceToString(uint32_t serviceId);
|
||||
static std::string gattStatusToString(esp_gatt_status_t status);
|
||||
static std::string getMember(uint32_t memberId);
|
||||
static void registerByAddress(BLEAddress address, BLEClient* pDevice);
|
||||
static void registerByConnId(uint16_t conn_id, BLEClient* pDevice);
|
||||
static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt);
|
||||
};
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */
|
139
libraries/BLE/src/BLEValue.cpp
Normal file
139
libraries/BLE/src/BLEValue.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* BLEValue.cpp
|
||||
*
|
||||
* Created on: Jul 17, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include "BLEValue.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG="BLEValue";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
BLEValue::BLEValue() {
|
||||
m_accumulation = "";
|
||||
m_value = "";
|
||||
m_readOffset = 0;
|
||||
} // BLEValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a message part to the accumulation.
|
||||
* The accumulation is a growing set of data that is added to until a commit or cancel.
|
||||
* @param [in] part A message part being added.
|
||||
*/
|
||||
void BLEValue::addPart(std::string part) {
|
||||
ESP_LOGD(LOG_TAG, ">> addPart: length=%d", part.length());
|
||||
m_accumulation += part;
|
||||
} // addPart
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a message part to the accumulation.
|
||||
* The accumulation is a growing set of data that is added to until a commit or cancel.
|
||||
* @param [in] pData A message part being added.
|
||||
* @param [in] length The number of bytes being added.
|
||||
*/
|
||||
void BLEValue::addPart(uint8_t* pData, size_t length) {
|
||||
ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length);
|
||||
m_accumulation += std::string((char*) pData, length);
|
||||
} // addPart
|
||||
|
||||
|
||||
/**
|
||||
* @brief Cancel the current accumulation.
|
||||
*/
|
||||
void BLEValue::cancel() {
|
||||
ESP_LOGD(LOG_TAG, ">> cancel");
|
||||
m_accumulation = "";
|
||||
m_readOffset = 0;
|
||||
} // cancel
|
||||
|
||||
|
||||
/**
|
||||
* @brief Commit the current accumulation.
|
||||
* When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces
|
||||
* of the overall message. After the last part has been received, we may perform a commit which means that
|
||||
* we now have the complete message and commit the change as a unit.
|
||||
*/
|
||||
void BLEValue::commit() {
|
||||
ESP_LOGD(LOG_TAG, ">> commit");
|
||||
// If there is nothing to commit, do nothing.
|
||||
if (m_accumulation.length() == 0) return;
|
||||
setValue(m_accumulation);
|
||||
m_accumulation = "";
|
||||
m_readOffset = 0;
|
||||
} // commit
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the data.
|
||||
* @return A pointer to the data.
|
||||
*/
|
||||
uint8_t* BLEValue::getData() {
|
||||
return (uint8_t*) m_value.data();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the length of the data in bytes.
|
||||
* @return The length of the data in bytes.
|
||||
*/
|
||||
size_t BLEValue::getLength() {
|
||||
return m_value.length();
|
||||
} // getLength
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the read offset.
|
||||
* @return The read offset into the read.
|
||||
*/
|
||||
uint16_t BLEValue::getReadOffset() {
|
||||
return m_readOffset;
|
||||
} // getReadOffset
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the current value.
|
||||
*/
|
||||
std::string BLEValue::getValue() {
|
||||
return m_value;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the read offset
|
||||
* @param [in] readOffset The offset into the read.
|
||||
*/
|
||||
void BLEValue::setReadOffset(uint16_t readOffset) {
|
||||
m_readOffset = readOffset;
|
||||
} // setReadOffset
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the current value.
|
||||
*/
|
||||
void BLEValue::setValue(std::string value) {
|
||||
m_value = value;
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the current value.
|
||||
* @param [in] pData The data for the current value.
|
||||
* @param [in] The length of the new current value.
|
||||
*/
|
||||
void BLEValue::setValue(uint8_t* pData, size_t length) {
|
||||
m_value = std::string((char*) pData, length);
|
||||
} // setValue
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
39
libraries/BLE/src/BLEValue.h
Normal file
39
libraries/BLE/src/BLEValue.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* BLEValue.h
|
||||
*
|
||||
* Created on: Jul 17, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEVALUE_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE value.
|
||||
*/
|
||||
class BLEValue {
|
||||
public:
|
||||
BLEValue();
|
||||
void addPart(std::string part);
|
||||
void addPart(uint8_t* pData, size_t length);
|
||||
void cancel();
|
||||
void commit();
|
||||
uint8_t* getData();
|
||||
size_t getLength();
|
||||
uint16_t getReadOffset();
|
||||
std::string getValue();
|
||||
void setReadOffset(uint16_t readOffset);
|
||||
void setValue(std::string value);
|
||||
void setValue(uint8_t* pData, size_t length);
|
||||
|
||||
private:
|
||||
std::string m_accumulation;
|
||||
uint16_t m_readOffset;
|
||||
std::string m_value;
|
||||
|
||||
};
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */
|
274
libraries/BLE/src/FreeRTOS.cpp
Normal file
274
libraries/BLE/src/FreeRTOS.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* FreeRTOS.cpp
|
||||
*
|
||||
* Created on: Feb 24, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions
|
||||
#include <freertos/task.h> // Include the task definitions
|
||||
#include <freertos/semphr.h> // Include the semaphore definitions
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "FreeRTOS";
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Sleep for the specified number of milliseconds.
|
||||
* @param[in] ms The period in milliseconds for which to sleep.
|
||||
*/
|
||||
void FreeRTOS::sleep(uint32_t ms) {
|
||||
::vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
} // sleep
|
||||
|
||||
|
||||
/**
|
||||
* Start a new task.
|
||||
* @param[in] task The function pointer to the function to be run in the task.
|
||||
* @param[in] taskName A string identifier for the task.
|
||||
* @param[in] param An optional parameter to be passed to the started task.
|
||||
* @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task.
|
||||
*/
|
||||
void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) {
|
||||
::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL);
|
||||
} // startTask
|
||||
|
||||
|
||||
/**
|
||||
* Delete the task.
|
||||
* @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted.
|
||||
*/
|
||||
void FreeRTOS::deleteTask(TaskHandle_t pTask) {
|
||||
::vTaskDelete(pTask);
|
||||
} // deleteTask
|
||||
|
||||
|
||||
/**
|
||||
* Get the time in milliseconds since the %FreeRTOS scheduler started.
|
||||
* @return The time in milliseconds since the %FreeRTOS scheduler started.
|
||||
*/
|
||||
uint32_t FreeRTOS::getTimeSinceStart() {
|
||||
return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS);
|
||||
} // getTimeSinceStart
|
||||
|
||||
|
||||
/**
|
||||
* @brief Wait for a semaphore to be released by trying to take it and
|
||||
* then releasing it again.
|
||||
* @param [in] owner A debug tag.
|
||||
* @return The value associated with the semaphore.
|
||||
*/
|
||||
uint32_t FreeRTOS::Semaphore::wait(std::string owner) {
|
||||
ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
|
||||
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_lock(&m_pthread_mutex);
|
||||
} else {
|
||||
xSemaphoreTake(m_semaphore, portMAX_DELAY);
|
||||
}
|
||||
|
||||
m_owner = owner;
|
||||
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_unlock(&m_pthread_mutex);
|
||||
} else {
|
||||
xSemaphoreGive(m_semaphore);
|
||||
}
|
||||
|
||||
ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str());
|
||||
m_owner = std::string("<N/A>");
|
||||
return m_value;
|
||||
} // wait
|
||||
|
||||
|
||||
FreeRTOS::Semaphore::Semaphore(std::string name) {
|
||||
m_usePthreads = false; // Are we using pThreads or FreeRTOS?
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_init(&m_pthread_mutex, nullptr);
|
||||
} else {
|
||||
m_semaphore = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
m_name = name;
|
||||
m_owner = std::string("<N/A>");
|
||||
m_value = 0;
|
||||
}
|
||||
|
||||
|
||||
FreeRTOS::Semaphore::~Semaphore() {
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_destroy(&m_pthread_mutex);
|
||||
} else {
|
||||
vSemaphoreDelete(m_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Give a semaphore.
|
||||
* The Semaphore is given.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::give() {
|
||||
ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str());
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_unlock(&m_pthread_mutex);
|
||||
} else {
|
||||
xSemaphoreGive(m_semaphore);
|
||||
}
|
||||
// #ifdef ARDUINO_ARCH_ESP32
|
||||
// FreeRTOS::sleep(10);
|
||||
// #endif
|
||||
|
||||
m_owner = std::string("<N/A>");
|
||||
} // Semaphore::give
|
||||
|
||||
|
||||
/**
|
||||
* @brief Give a semaphore.
|
||||
* The Semaphore is given with an associated value.
|
||||
* @param [in] value The value to associate with the semaphore.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::give(uint32_t value) {
|
||||
m_value = value;
|
||||
give();
|
||||
} // give
|
||||
|
||||
|
||||
/**
|
||||
* @brief Give a semaphore from an ISR.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::giveFromISR() {
|
||||
BaseType_t higherPriorityTaskWoken;
|
||||
if (m_usePthreads) {
|
||||
assert(false);
|
||||
} else {
|
||||
xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken);
|
||||
}
|
||||
} // giveFromISR
|
||||
|
||||
|
||||
/**
|
||||
* @brief Take a semaphore.
|
||||
* Take a semaphore and wait indefinitely.
|
||||
* @param [in] owner The new owner (for debugging)
|
||||
* @return True if we took the semaphore.
|
||||
*/
|
||||
bool FreeRTOS::Semaphore::take(std::string owner) {
|
||||
ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
|
||||
bool rc = false;
|
||||
if (m_usePthreads) {
|
||||
pthread_mutex_lock(&m_pthread_mutex);
|
||||
} else {
|
||||
rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE;
|
||||
}
|
||||
m_owner = owner;
|
||||
if (rc) {
|
||||
ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
|
||||
} else {
|
||||
ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
|
||||
}
|
||||
return rc;
|
||||
} // Semaphore::take
|
||||
|
||||
|
||||
/**
|
||||
* @brief Take a semaphore.
|
||||
* Take a semaphore but return if we haven't obtained it in the given period of milliseconds.
|
||||
* @param [in] timeoutMs Timeout in milliseconds.
|
||||
* @param [in] owner The new owner (for debugging)
|
||||
* @return True if we took the semaphore.
|
||||
*/
|
||||
bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
|
||||
ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
|
||||
bool rc = false;
|
||||
if (m_usePthreads) {
|
||||
assert(false); // We apparently don't have a timed wait for pthreads.
|
||||
} else {
|
||||
rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE;
|
||||
}
|
||||
m_owner = owner;
|
||||
if (rc) {
|
||||
ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str());
|
||||
} else {
|
||||
ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
|
||||
}
|
||||
return rc;
|
||||
} // Semaphore::take
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a string representation of the semaphore.
|
||||
* @return A string representation of the semaphore.
|
||||
*/
|
||||
std::string FreeRTOS::Semaphore::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner;
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the name of the semaphore.
|
||||
* @param [in] name The name of the semaphore.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::setName(std::string name) {
|
||||
m_name = name;
|
||||
} // setName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a ring buffer.
|
||||
* @param [in] length The amount of storage to allocate for the ring buffer.
|
||||
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
|
||||
*/
|
||||
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
|
||||
m_handle = ::xRingbufferCreate(length, type);
|
||||
} // Ringbuffer
|
||||
|
||||
|
||||
Ringbuffer::~Ringbuffer() {
|
||||
::vRingbufferDelete(m_handle);
|
||||
} // ~Ringbuffer
|
||||
|
||||
|
||||
/**
|
||||
* @brief Receive data from the buffer.
|
||||
* @param [out] size On return, the size of data returned.
|
||||
* @param [in] wait How long to wait.
|
||||
* @return A pointer to the storage retrieved.
|
||||
*/
|
||||
void* Ringbuffer::receive(size_t* size, TickType_t wait) {
|
||||
return ::xRingbufferReceive(m_handle, size, wait);
|
||||
} // receive
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return an item.
|
||||
* @param [in] item The item to be returned/released.
|
||||
*/
|
||||
void Ringbuffer::returnItem(void* item) {
|
||||
::vRingbufferReturnItem(m_handle, item);
|
||||
} // returnItem
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send data to the buffer.
|
||||
* @param [in] data The data to place into the buffer.
|
||||
* @param [in] length The length of data to place into the buffer.
|
||||
* @param [in] wait How long to wait before giving up. The default is to wait indefinitely.
|
||||
* @return
|
||||
*/
|
||||
bool Ringbuffer::send(void* data, size_t length, TickType_t wait) {
|
||||
return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE;
|
||||
} // send
|
||||
|
||||
|
71
libraries/BLE/src/FreeRTOS.h
Normal file
71
libraries/BLE/src/FreeRTOS.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* FreeRTOS.h
|
||||
*
|
||||
* Created on: Feb 24, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef MAIN_FREERTOS_H_
|
||||
#define MAIN_FREERTOS_H_
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions.
|
||||
#include <freertos/task.h> // Include the task definitions.
|
||||
#include <freertos/semphr.h> // Include the semaphore definitions.
|
||||
#include <freertos/ringbuf.h> // Include the ringbuffer definitions.
|
||||
|
||||
|
||||
/**
|
||||
* @brief Interface to %FreeRTOS functions.
|
||||
*/
|
||||
class FreeRTOS {
|
||||
public:
|
||||
static void sleep(uint32_t ms);
|
||||
static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048);
|
||||
static void deleteTask(TaskHandle_t pTask = nullptr);
|
||||
|
||||
static uint32_t getTimeSinceStart();
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
Semaphore(std::string owner = "<Unknown>");
|
||||
~Semaphore();
|
||||
void give();
|
||||
void give(uint32_t value);
|
||||
void giveFromISR();
|
||||
void setName(std::string name);
|
||||
bool take(std::string owner = "<Unknown>");
|
||||
bool take(uint32_t timeoutMs, std::string owner = "<Unknown>");
|
||||
std::string toString();
|
||||
uint32_t wait(std::string owner = "<Unknown>");
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t m_semaphore;
|
||||
pthread_mutex_t m_pthread_mutex;
|
||||
std::string m_name;
|
||||
std::string m_owner;
|
||||
uint32_t m_value;
|
||||
bool m_usePthreads;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ringbuffer.
|
||||
*/
|
||||
class Ringbuffer {
|
||||
public:
|
||||
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
|
||||
~Ringbuffer();
|
||||
|
||||
void* receive(size_t* size, TickType_t wait = portMAX_DELAY);
|
||||
void returnItem(void* item);
|
||||
bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY);
|
||||
private:
|
||||
RingbufHandle_t m_handle;
|
||||
};
|
||||
|
||||
#endif /* MAIN_FREERTOS_H_ */
|
544
libraries/BLE/src/GeneralUtils.cpp
Normal file
544
libraries/BLE/src/GeneralUtils.cpp
Normal file
@ -0,0 +1,544 @@
|
||||
/*
|
||||
* GeneralUtils.cpp
|
||||
*
|
||||
* Created on: May 20, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
#include <esp_system.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "FreeRTOS.h"
|
||||
#include <esp_err.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "GeneralUtils";
|
||||
#endif
|
||||
|
||||
|
||||
static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static int base64EncodedLength(size_t length) {
|
||||
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
|
||||
} // base64EncodedLength
|
||||
|
||||
|
||||
static int base64EncodedLength(const std::string& in) {
|
||||
return base64EncodedLength(in.length());
|
||||
} // base64EncodedLength
|
||||
|
||||
|
||||
static void a3_to_a4(unsigned char* a4, unsigned char* a3) {
|
||||
a4[0] = (a3[0] & 0xfc) >> 2;
|
||||
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
||||
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
||||
a4[3] = (a3[2] & 0x3f);
|
||||
} // a3_to_a4
|
||||
|
||||
|
||||
static void a4_to_a3(unsigned char* a3, unsigned char* a4) {
|
||||
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
||||
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
||||
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
||||
} // a4_to_a3
|
||||
|
||||
|
||||
/**
|
||||
* @brief Encode a string into base 64.
|
||||
* @param [in] in
|
||||
* @param [out] out
|
||||
*/
|
||||
bool GeneralUtils::base64Encode(const std::string& in, std::string* out) {
|
||||
int i = 0, j = 0;
|
||||
size_t enc_len = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
|
||||
out->resize(base64EncodedLength(in));
|
||||
|
||||
int input_len = in.size();
|
||||
std::string::const_iterator input = in.begin();
|
||||
|
||||
while (input_len--) {
|
||||
a3[i++] = *(input++);
|
||||
if (i == 3) {
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (j = 0; j < i + 1; j++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
|
||||
}
|
||||
|
||||
while ((i++ < 3)) {
|
||||
(*out)[enc_len++] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
return (enc_len == out->size());
|
||||
} // base64Encode
|
||||
|
||||
|
||||
/**
|
||||
* @brief Dump general info to the log.
|
||||
* Data includes:
|
||||
* * Amount of free RAM
|
||||
*/
|
||||
void GeneralUtils::dumpInfo() {
|
||||
size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
ESP_LOGV(LOG_TAG, "--- dumpInfo ---");
|
||||
ESP_LOGV(LOG_TAG, "Free heap: %d", freeHeap);
|
||||
ESP_LOGV(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision);
|
||||
ESP_LOGV(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version());
|
||||
ESP_LOGV(LOG_TAG, "---");
|
||||
} // dumpInfo
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the string end with a specific character?
|
||||
* @param [in] str The string to examine.
|
||||
* @param [in] c The character to look form.
|
||||
* @return True if the string ends with the given character.
|
||||
*/
|
||||
bool GeneralUtils::endsWith(std::string str, char c) {
|
||||
if (str.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (str.at(str.length() - 1) == c) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // endsWidth
|
||||
|
||||
|
||||
static int DecodedLength(const std::string& in) {
|
||||
int numEq = 0;
|
||||
int n = (int) in.size();
|
||||
|
||||
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) {
|
||||
++numEq;
|
||||
}
|
||||
return ((6 * n) / 8) - numEq;
|
||||
} // DecodedLength
|
||||
|
||||
|
||||
static unsigned char b64_lookup(unsigned char c) {
|
||||
if(c >='A' && c <='Z') return c - 'A';
|
||||
if(c >='a' && c <='z') return c - 71;
|
||||
if(c >='0' && c <='9') return c + 4;
|
||||
if(c == '+') return 62;
|
||||
if(c == '/') return 63;
|
||||
return 255;
|
||||
}; // b64_lookup
|
||||
|
||||
|
||||
/**
|
||||
* @brief Decode a chunk of data that is base64 encoded.
|
||||
* @param [in] in The string to be decoded.
|
||||
* @param [out] out The resulting data.
|
||||
*/
|
||||
bool GeneralUtils::base64Decode(const std::string& in, std::string* out) {
|
||||
int i = 0, j = 0;
|
||||
size_t dec_len = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
|
||||
int input_len = in.size();
|
||||
std::string::const_iterator input = in.begin();
|
||||
|
||||
out->resize(DecodedLength(in));
|
||||
|
||||
while (input_len--) {
|
||||
if (*input == '=') {
|
||||
break;
|
||||
}
|
||||
|
||||
a4[i++] = *(input++);
|
||||
if (i == 4) {
|
||||
for (i = 0; i <4; i++) {
|
||||
a4[i] = b64_lookup(a4[i]);
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
(*out)[dec_len++] = a3[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
(*out)[dec_len++] = a3[j];
|
||||
}
|
||||
}
|
||||
|
||||
return (dec_len == out->size());
|
||||
} // base64Decode
|
||||
|
||||
/*
|
||||
void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) {
|
||||
uint32_t index=0;
|
||||
std::stringstream ascii;
|
||||
std::stringstream hex;
|
||||
char asciiBuf[80];
|
||||
char hexBuf[80];
|
||||
hex.str("");
|
||||
ascii.str("");
|
||||
while(index < length) {
|
||||
hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' ';
|
||||
if (std::isprint(pData[index])) {
|
||||
ascii << pData[index];
|
||||
} else {
|
||||
ascii << '.';
|
||||
}
|
||||
index++;
|
||||
if (index % 16 == 0) {
|
||||
strcpy(hexBuf, hex.str().c_str());
|
||||
strcpy(asciiBuf, ascii.str().c_str());
|
||||
ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf);
|
||||
hex.str("");
|
||||
ascii.str("");
|
||||
}
|
||||
}
|
||||
if (index %16 != 0) {
|
||||
while(index % 16 != 0) {
|
||||
hex << " ";
|
||||
index++;
|
||||
}
|
||||
strcpy(hexBuf, hex.str().c_str());
|
||||
strcpy(asciiBuf, ascii.str().c_str());
|
||||
ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf);
|
||||
//ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
|
||||
}
|
||||
FreeRTOS::sleep(1000);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) {
|
||||
uint32_t index=0;
|
||||
static std::stringstream ascii;
|
||||
static std::stringstream hex;
|
||||
hex.str("");
|
||||
ascii.str("");
|
||||
while(index < length) {
|
||||
hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' ';
|
||||
if (std::isprint(pData[index])) {
|
||||
ascii << pData[index];
|
||||
} else {
|
||||
ascii << '.';
|
||||
}
|
||||
index++;
|
||||
if (index % 16 == 0) {
|
||||
ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
|
||||
hex.str("");
|
||||
ascii.str("");
|
||||
}
|
||||
}
|
||||
if (index %16 != 0) {
|
||||
while(index % 16 != 0) {
|
||||
hex << " ";
|
||||
index++;
|
||||
}
|
||||
ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
|
||||
}
|
||||
FreeRTOS::sleep(1000);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Dump a representation of binary data to the console.
|
||||
*
|
||||
* @param [in] pData Pointer to the start of data to be logged.
|
||||
* @param [in] length Length of the data (in bytes) to be logged.
|
||||
* @return N/A.
|
||||
*/
|
||||
void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) {
|
||||
char ascii[80];
|
||||
char hex[80];
|
||||
char tempBuf[80];
|
||||
uint32_t lineNumber = 0;
|
||||
|
||||
ESP_LOGV(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f");
|
||||
ESP_LOGV(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
|
||||
strcpy(ascii, "");
|
||||
strcpy(hex, "");
|
||||
uint32_t index = 0;
|
||||
while (index < length) {
|
||||
sprintf(tempBuf, "%.2x ", pData[index]);
|
||||
strcat(hex, tempBuf);
|
||||
if (isprint(pData[index])) {
|
||||
sprintf(tempBuf, "%c", pData[index]);
|
||||
} else {
|
||||
sprintf(tempBuf, ".");
|
||||
}
|
||||
strcat(ascii, tempBuf);
|
||||
index++;
|
||||
if (index % 16 == 0) {
|
||||
ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii);
|
||||
strcpy(ascii, "");
|
||||
strcpy(hex, "");
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
if (index %16 != 0) {
|
||||
while (index % 16 != 0) {
|
||||
strcat(hex, " ");
|
||||
index++;
|
||||
}
|
||||
ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii);
|
||||
}
|
||||
} // hexDump
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert an IP address to string.
|
||||
* @param ip The 4 byte IP address.
|
||||
* @return A string representation of the IP address.
|
||||
*/
|
||||
std::string GeneralUtils::ipToString(uint8_t *ip) {
|
||||
std::stringstream s;
|
||||
s << (int) ip[0] << '.' << (int) ip[1] << '.' << (int) ip[2] << '.' << (int) ip[3];
|
||||
return s.str();
|
||||
} // ipToString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Split a string into parts based on a delimiter.
|
||||
* @param [in] source The source string to split.
|
||||
* @param [in] delimiter The delimiter characters.
|
||||
* @return A vector of strings that are the split of the input.
|
||||
*/
|
||||
std::vector<std::string> GeneralUtils::split(std::string source, char delimiter) {
|
||||
// See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g
|
||||
std::vector<std::string> strings;
|
||||
std::istringstream iss(source);
|
||||
std::string s;
|
||||
while (std::getline(iss, s, delimiter)) {
|
||||
strings.push_back(trim(s));
|
||||
}
|
||||
return strings;
|
||||
} // split
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert an ESP error code to a string.
|
||||
* @param [in] errCode The errCode to be converted.
|
||||
* @return A string representation of the error code.
|
||||
*/
|
||||
const char* GeneralUtils::errorToString(esp_err_t errCode) {
|
||||
switch (errCode) {
|
||||
#if CONFIG_LOG_DEFAULT_LEVEL > 4
|
||||
case ESP_OK:
|
||||
return "ESP_OK";
|
||||
case ESP_FAIL:
|
||||
return "ESP_FAIL";
|
||||
case ESP_ERR_NO_MEM:
|
||||
return "ESP_ERR_NO_MEM";
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
return "ESP_ERR_INVALID_ARG";
|
||||
case ESP_ERR_INVALID_SIZE:
|
||||
return "ESP_ERR_INVALID_SIZE";
|
||||
case ESP_ERR_INVALID_STATE:
|
||||
return "ESP_ERR_INVALID_STATE";
|
||||
case ESP_ERR_NOT_FOUND:
|
||||
return "ESP_ERR_NOT_FOUND";
|
||||
case ESP_ERR_NOT_SUPPORTED:
|
||||
return "ESP_ERR_NOT_SUPPORTED";
|
||||
case ESP_ERR_TIMEOUT:
|
||||
return "ESP_ERR_TIMEOUT";
|
||||
case ESP_ERR_NVS_NOT_INITIALIZED:
|
||||
return "ESP_ERR_NVS_NOT_INITIALIZED";
|
||||
case ESP_ERR_NVS_NOT_FOUND:
|
||||
return "ESP_ERR_NVS_NOT_FOUND";
|
||||
case ESP_ERR_NVS_TYPE_MISMATCH:
|
||||
return "ESP_ERR_NVS_TYPE_MISMATCH";
|
||||
case ESP_ERR_NVS_READ_ONLY:
|
||||
return "ESP_ERR_NVS_READ_ONLY";
|
||||
case ESP_ERR_NVS_NOT_ENOUGH_SPACE:
|
||||
return "ESP_ERR_NVS_NOT_ENOUGH_SPACE";
|
||||
case ESP_ERR_NVS_INVALID_NAME:
|
||||
return "ESP_ERR_NVS_INVALID_NAME";
|
||||
case ESP_ERR_NVS_INVALID_HANDLE:
|
||||
return "ESP_ERR_NVS_INVALID_HANDLE";
|
||||
case ESP_ERR_NVS_REMOVE_FAILED:
|
||||
return "ESP_ERR_NVS_REMOVE_FAILED";
|
||||
case ESP_ERR_NVS_KEY_TOO_LONG:
|
||||
return "ESP_ERR_NVS_KEY_TOO_LONG";
|
||||
case ESP_ERR_NVS_PAGE_FULL:
|
||||
return "ESP_ERR_NVS_PAGE_FULL";
|
||||
case ESP_ERR_NVS_INVALID_STATE:
|
||||
return "ESP_ERR_NVS_INVALID_STATE";
|
||||
case ESP_ERR_NVS_INVALID_LENGTH:
|
||||
return "ESP_ERR_NVS_INVALID_LENGTH";
|
||||
case ESP_ERR_WIFI_NOT_INIT:
|
||||
return "ESP_ERR_WIFI_NOT_INIT";
|
||||
//case ESP_ERR_WIFI_NOT_START:
|
||||
// return "ESP_ERR_WIFI_NOT_START";
|
||||
case ESP_ERR_WIFI_IF:
|
||||
return "ESP_ERR_WIFI_IF";
|
||||
case ESP_ERR_WIFI_MODE:
|
||||
return "ESP_ERR_WIFI_MODE";
|
||||
case ESP_ERR_WIFI_STATE:
|
||||
return "ESP_ERR_WIFI_STATE";
|
||||
case ESP_ERR_WIFI_CONN:
|
||||
return "ESP_ERR_WIFI_CONN";
|
||||
case ESP_ERR_WIFI_NVS:
|
||||
return "ESP_ERR_WIFI_NVS";
|
||||
case ESP_ERR_WIFI_MAC:
|
||||
return "ESP_ERR_WIFI_MAC";
|
||||
case ESP_ERR_WIFI_SSID:
|
||||
return "ESP_ERR_WIFI_SSID";
|
||||
case ESP_ERR_WIFI_PASSWORD:
|
||||
return "ESP_ERR_WIFI_PASSWORD";
|
||||
case ESP_ERR_WIFI_TIMEOUT:
|
||||
return "ESP_ERR_WIFI_TIMEOUT";
|
||||
case ESP_ERR_WIFI_WAKE_FAIL:
|
||||
return "ESP_ERR_WIFI_WAKE_FAIL";
|
||||
#endif
|
||||
default:
|
||||
return "Unknown ESP_ERR error";
|
||||
}
|
||||
} // errorToString
|
||||
|
||||
/**
|
||||
* @brief Convert a wifi_err_reason_t code to a string.
|
||||
* @param [in] errCode The errCode to be converted.
|
||||
* @return A string representation of the error code.
|
||||
*
|
||||
* @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h.
|
||||
*/
|
||||
const char* GeneralUtils::wifiErrorToString(uint8_t errCode) {
|
||||
if (errCode == ESP_OK) return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)";
|
||||
if (errCode == UINT8_MAX) return "Not Connected (default value)";
|
||||
|
||||
switch ((wifi_err_reason_t) errCode) {
|
||||
#if CONFIG_LOG_DEFAULT_LEVEL > 4
|
||||
case WIFI_REASON_UNSPECIFIED:
|
||||
return "WIFI_REASON_UNSPECIFIED";
|
||||
case WIFI_REASON_AUTH_EXPIRE:
|
||||
return "WIFI_REASON_AUTH_EXPIRE";
|
||||
case WIFI_REASON_AUTH_LEAVE:
|
||||
return "WIFI_REASON_AUTH_LEAVE";
|
||||
case WIFI_REASON_ASSOC_EXPIRE:
|
||||
return "WIFI_REASON_ASSOC_EXPIRE";
|
||||
case WIFI_REASON_ASSOC_TOOMANY:
|
||||
return "WIFI_REASON_ASSOC_TOOMANY";
|
||||
case WIFI_REASON_NOT_AUTHED:
|
||||
return "WIFI_REASON_NOT_AUTHED";
|
||||
case WIFI_REASON_NOT_ASSOCED:
|
||||
return "WIFI_REASON_NOT_ASSOCED";
|
||||
case WIFI_REASON_ASSOC_LEAVE:
|
||||
return "WIFI_REASON_ASSOC_LEAVE";
|
||||
case WIFI_REASON_ASSOC_NOT_AUTHED:
|
||||
return "WIFI_REASON_ASSOC_NOT_AUTHED";
|
||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD:
|
||||
return "WIFI_REASON_DISASSOC_PWRCAP_BAD";
|
||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD:
|
||||
return "WIFI_REASON_DISASSOC_SUPCHAN_BAD";
|
||||
case WIFI_REASON_IE_INVALID:
|
||||
return "WIFI_REASON_IE_INVALID";
|
||||
case WIFI_REASON_MIC_FAILURE:
|
||||
return "WIFI_REASON_MIC_FAILURE";
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
|
||||
return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT";
|
||||
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT:
|
||||
return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT";
|
||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS:
|
||||
return "WIFI_REASON_IE_IN_4WAY_DIFFERS";
|
||||
case WIFI_REASON_GROUP_CIPHER_INVALID:
|
||||
return "WIFI_REASON_GROUP_CIPHER_INVALID";
|
||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID:
|
||||
return "WIFI_REASON_PAIRWISE_CIPHER_INVALID";
|
||||
case WIFI_REASON_AKMP_INVALID:
|
||||
return "WIFI_REASON_AKMP_INVALID";
|
||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION:
|
||||
return "WIFI_REASON_UNSUPP_RSN_IE_VERSION";
|
||||
case WIFI_REASON_INVALID_RSN_IE_CAP:
|
||||
return "WIFI_REASON_INVALID_RSN_IE_CAP";
|
||||
case WIFI_REASON_802_1X_AUTH_FAILED:
|
||||
return "WIFI_REASON_802_1X_AUTH_FAILED";
|
||||
case WIFI_REASON_CIPHER_SUITE_REJECTED:
|
||||
return "WIFI_REASON_CIPHER_SUITE_REJECTED";
|
||||
case WIFI_REASON_BEACON_TIMEOUT:
|
||||
return "WIFI_REASON_BEACON_TIMEOUT";
|
||||
case WIFI_REASON_NO_AP_FOUND:
|
||||
return "WIFI_REASON_NO_AP_FOUND";
|
||||
case WIFI_REASON_AUTH_FAIL:
|
||||
return "WIFI_REASON_AUTH_FAIL";
|
||||
case WIFI_REASON_ASSOC_FAIL:
|
||||
return "WIFI_REASON_ASSOC_FAIL";
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT:
|
||||
return "WIFI_REASON_HANDSHAKE_TIMEOUT";
|
||||
#endif
|
||||
default:
|
||||
return "Unknown ESP_ERR error";
|
||||
}
|
||||
} // wifiErrorToString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a string to lower case.
|
||||
* @param [in] value The string to convert to lower case.
|
||||
* @return A lower case representation of the string.
|
||||
*/
|
||||
std::string GeneralUtils::toLower(std::string& value) {
|
||||
// Question: Could this be improved with a signature of:
|
||||
// std::string& GeneralUtils::toLower(std::string& value)
|
||||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||||
return value;
|
||||
} // toLower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Remove white space from a string.
|
||||
*/
|
||||
std::string GeneralUtils::trim(const std::string& str) {
|
||||
size_t first = str.find_first_not_of(' ');
|
||||
if (std::string::npos == first) return str;
|
||||
size_t last = str.find_last_not_of(' ');
|
||||
return str.substr(first, (last - first + 1));
|
||||
} // trim
|
35
libraries/BLE/src/GeneralUtils.h
Normal file
35
libraries/BLE/src/GeneralUtils.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* GeneralUtils.h
|
||||
*
|
||||
* Created on: May 20, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_GENERALUTILS_H_
|
||||
#define COMPONENTS_CPP_UTILS_GENERALUTILS_H_
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <esp_err.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief General utilities.
|
||||
*/
|
||||
class GeneralUtils {
|
||||
public:
|
||||
static bool base64Decode(const std::string& in, std::string* out);
|
||||
static bool base64Encode(const std::string& in, std::string* out);
|
||||
static void dumpInfo();
|
||||
static bool endsWith(std::string str, char c);
|
||||
static const char* errorToString(esp_err_t errCode);
|
||||
static const char* wifiErrorToString(uint8_t value);
|
||||
static void hexDump(const uint8_t* pData, uint32_t length);
|
||||
static std::string ipToString(uint8_t* ip);
|
||||
static std::vector<std::string> split(std::string source, char delimiter);
|
||||
static std::string toLower(std::string& value);
|
||||
static std::string trim(const std::string& str);
|
||||
|
||||
};
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */
|
402
libraries/BLE/src/HIDKeyboardTypes.h
Normal file
402
libraries/BLE/src/HIDKeyboardTypes.h
Normal file
@ -0,0 +1,402 @@
|
||||
/* Copyright (c) 2015 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Note: this file was pulled from different parts of the USBHID library, in mbed SDK
|
||||
*/
|
||||
|
||||
#ifndef KEYBOARD_DEFS_H
|
||||
#define KEYBOARD_DEFS_H
|
||||
|
||||
#define REPORT_ID_KEYBOARD 1
|
||||
#define REPORT_ID_VOLUME 3
|
||||
|
||||
/* Modifiers */
|
||||
enum MODIFIER_KEY {
|
||||
KEY_CTRL = 1,
|
||||
KEY_SHIFT = 2,
|
||||
KEY_ALT = 4,
|
||||
};
|
||||
|
||||
|
||||
enum MEDIA_KEY {
|
||||
KEY_NEXT_TRACK, /*!< next Track Button */
|
||||
KEY_PREVIOUS_TRACK, /*!< Previous track Button */
|
||||
KEY_STOP, /*!< Stop Button */
|
||||
KEY_PLAY_PAUSE, /*!< Play/Pause Button */
|
||||
KEY_MUTE, /*!< Mute Button */
|
||||
KEY_VOLUME_UP, /*!< Volume Up Button */
|
||||
KEY_VOLUME_DOWN, /*!< Volume Down Button */
|
||||
};
|
||||
|
||||
enum FUNCTION_KEY {
|
||||
KEY_F1 = 128, /* F1 key */
|
||||
KEY_F2, /* F2 key */
|
||||
KEY_F3, /* F3 key */
|
||||
KEY_F4, /* F4 key */
|
||||
KEY_F5, /* F5 key */
|
||||
KEY_F6, /* F6 key */
|
||||
KEY_F7, /* F7 key */
|
||||
KEY_F8, /* F8 key */
|
||||
KEY_F9, /* F9 key */
|
||||
KEY_F10, /* F10 key */
|
||||
KEY_F11, /* F11 key */
|
||||
KEY_F12, /* F12 key */
|
||||
|
||||
KEY_PRINT_SCREEN, /* Print Screen key */
|
||||
KEY_SCROLL_LOCK, /* Scroll lock */
|
||||
KEY_CAPS_LOCK, /* caps lock */
|
||||
KEY_NUM_LOCK, /* num lock */
|
||||
KEY_INSERT, /* Insert key */
|
||||
KEY_HOME, /* Home key */
|
||||
KEY_PAGE_UP, /* Page Up key */
|
||||
KEY_PAGE_DOWN, /* Page Down key */
|
||||
|
||||
RIGHT_ARROW, /* Right arrow */
|
||||
LEFT_ARROW, /* Left arrow */
|
||||
DOWN_ARROW, /* Down arrow */
|
||||
UP_ARROW, /* Up arrow */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
unsigned char usage;
|
||||
unsigned char modifier;
|
||||
} KEYMAP;
|
||||
|
||||
#ifdef US_KEYBOARD
|
||||
/* US keyboard (as HID standard) */
|
||||
#define KEYMAP_SIZE (152)
|
||||
const KEYMAP keymap[KEYMAP_SIZE] = {
|
||||
{0, 0}, /* NUL */
|
||||
{0, 0}, /* SOH */
|
||||
{0, 0}, /* STX */
|
||||
{0, 0}, /* ETX */
|
||||
{0, 0}, /* EOT */
|
||||
{0, 0}, /* ENQ */
|
||||
{0, 0}, /* ACK */
|
||||
{0, 0}, /* BEL */
|
||||
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
|
||||
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
|
||||
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
|
||||
{0, 0}, /* VT */
|
||||
{0, 0}, /* FF */
|
||||
{0, 0}, /* CR */
|
||||
{0, 0}, /* SO */
|
||||
{0, 0}, /* SI */
|
||||
{0, 0}, /* DEL */
|
||||
{0, 0}, /* DC1 */
|
||||
{0, 0}, /* DC2 */
|
||||
{0, 0}, /* DC3 */
|
||||
{0, 0}, /* DC4 */
|
||||
{0, 0}, /* NAK */
|
||||
{0, 0}, /* SYN */
|
||||
{0, 0}, /* ETB */
|
||||
{0, 0}, /* CAN */
|
||||
{0, 0}, /* EM */
|
||||
{0, 0}, /* SUB */
|
||||
{0, 0}, /* ESC */
|
||||
{0, 0}, /* FS */
|
||||
{0, 0}, /* GS */
|
||||
{0, 0}, /* RS */
|
||||
{0, 0}, /* US */
|
||||
{0x2c, 0}, /* */
|
||||
{0x1e, KEY_SHIFT}, /* ! */
|
||||
{0x34, KEY_SHIFT}, /* " */
|
||||
{0x20, KEY_SHIFT}, /* # */
|
||||
{0x21, KEY_SHIFT}, /* $ */
|
||||
{0x22, KEY_SHIFT}, /* % */
|
||||
{0x24, KEY_SHIFT}, /* & */
|
||||
{0x34, 0}, /* ' */
|
||||
{0x26, KEY_SHIFT}, /* ( */
|
||||
{0x27, KEY_SHIFT}, /* ) */
|
||||
{0x25, KEY_SHIFT}, /* * */
|
||||
{0x2e, KEY_SHIFT}, /* + */
|
||||
{0x36, 0}, /* , */
|
||||
{0x2d, 0}, /* - */
|
||||
{0x37, 0}, /* . */
|
||||
{0x38, 0}, /* / */
|
||||
{0x27, 0}, /* 0 */
|
||||
{0x1e, 0}, /* 1 */
|
||||
{0x1f, 0}, /* 2 */
|
||||
{0x20, 0}, /* 3 */
|
||||
{0x21, 0}, /* 4 */
|
||||
{0x22, 0}, /* 5 */
|
||||
{0x23, 0}, /* 6 */
|
||||
{0x24, 0}, /* 7 */
|
||||
{0x25, 0}, /* 8 */
|
||||
{0x26, 0}, /* 9 */
|
||||
{0x33, KEY_SHIFT}, /* : */
|
||||
{0x33, 0}, /* ; */
|
||||
{0x36, KEY_SHIFT}, /* < */
|
||||
{0x2e, 0}, /* = */
|
||||
{0x37, KEY_SHIFT}, /* > */
|
||||
{0x38, KEY_SHIFT}, /* ? */
|
||||
{0x1f, KEY_SHIFT}, /* @ */
|
||||
{0x04, KEY_SHIFT}, /* A */
|
||||
{0x05, KEY_SHIFT}, /* B */
|
||||
{0x06, KEY_SHIFT}, /* C */
|
||||
{0x07, KEY_SHIFT}, /* D */
|
||||
{0x08, KEY_SHIFT}, /* E */
|
||||
{0x09, KEY_SHIFT}, /* F */
|
||||
{0x0a, KEY_SHIFT}, /* G */
|
||||
{0x0b, KEY_SHIFT}, /* H */
|
||||
{0x0c, KEY_SHIFT}, /* I */
|
||||
{0x0d, KEY_SHIFT}, /* J */
|
||||
{0x0e, KEY_SHIFT}, /* K */
|
||||
{0x0f, KEY_SHIFT}, /* L */
|
||||
{0x10, KEY_SHIFT}, /* M */
|
||||
{0x11, KEY_SHIFT}, /* N */
|
||||
{0x12, KEY_SHIFT}, /* O */
|
||||
{0x13, KEY_SHIFT}, /* P */
|
||||
{0x14, KEY_SHIFT}, /* Q */
|
||||
{0x15, KEY_SHIFT}, /* R */
|
||||
{0x16, KEY_SHIFT}, /* S */
|
||||
{0x17, KEY_SHIFT}, /* T */
|
||||
{0x18, KEY_SHIFT}, /* U */
|
||||
{0x19, KEY_SHIFT}, /* V */
|
||||
{0x1a, KEY_SHIFT}, /* W */
|
||||
{0x1b, KEY_SHIFT}, /* X */
|
||||
{0x1c, KEY_SHIFT}, /* Y */
|
||||
{0x1d, KEY_SHIFT}, /* Z */
|
||||
{0x2f, 0}, /* [ */
|
||||
{0x31, 0}, /* \ */
|
||||
{0x30, 0}, /* ] */
|
||||
{0x23, KEY_SHIFT}, /* ^ */
|
||||
{0x2d, KEY_SHIFT}, /* _ */
|
||||
{0x35, 0}, /* ` */
|
||||
{0x04, 0}, /* a */
|
||||
{0x05, 0}, /* b */
|
||||
{0x06, 0}, /* c */
|
||||
{0x07, 0}, /* d */
|
||||
{0x08, 0}, /* e */
|
||||
{0x09, 0}, /* f */
|
||||
{0x0a, 0}, /* g */
|
||||
{0x0b, 0}, /* h */
|
||||
{0x0c, 0}, /* i */
|
||||
{0x0d, 0}, /* j */
|
||||
{0x0e, 0}, /* k */
|
||||
{0x0f, 0}, /* l */
|
||||
{0x10, 0}, /* m */
|
||||
{0x11, 0}, /* n */
|
||||
{0x12, 0}, /* o */
|
||||
{0x13, 0}, /* p */
|
||||
{0x14, 0}, /* q */
|
||||
{0x15, 0}, /* r */
|
||||
{0x16, 0}, /* s */
|
||||
{0x17, 0}, /* t */
|
||||
{0x18, 0}, /* u */
|
||||
{0x19, 0}, /* v */
|
||||
{0x1a, 0}, /* w */
|
||||
{0x1b, 0}, /* x */
|
||||
{0x1c, 0}, /* y */
|
||||
{0x1d, 0}, /* z */
|
||||
{0x2f, KEY_SHIFT}, /* { */
|
||||
{0x31, KEY_SHIFT}, /* | */
|
||||
{0x30, KEY_SHIFT}, /* } */
|
||||
{0x35, KEY_SHIFT}, /* ~ */
|
||||
{0,0}, /* DEL */
|
||||
|
||||
{0x3a, 0}, /* F1 */
|
||||
{0x3b, 0}, /* F2 */
|
||||
{0x3c, 0}, /* F3 */
|
||||
{0x3d, 0}, /* F4 */
|
||||
{0x3e, 0}, /* F5 */
|
||||
{0x3f, 0}, /* F6 */
|
||||
{0x40, 0}, /* F7 */
|
||||
{0x41, 0}, /* F8 */
|
||||
{0x42, 0}, /* F9 */
|
||||
{0x43, 0}, /* F10 */
|
||||
{0x44, 0}, /* F11 */
|
||||
{0x45, 0}, /* F12 */
|
||||
|
||||
{0x46, 0}, /* PRINT_SCREEN */
|
||||
{0x47, 0}, /* SCROLL_LOCK */
|
||||
{0x39, 0}, /* CAPS_LOCK */
|
||||
{0x53, 0}, /* NUM_LOCK */
|
||||
{0x49, 0}, /* INSERT */
|
||||
{0x4a, 0}, /* HOME */
|
||||
{0x4b, 0}, /* PAGE_UP */
|
||||
{0x4e, 0}, /* PAGE_DOWN */
|
||||
|
||||
{0x4f, 0}, /* RIGHT_ARROW */
|
||||
{0x50, 0}, /* LEFT_ARROW */
|
||||
{0x51, 0}, /* DOWN_ARROW */
|
||||
{0x52, 0}, /* UP_ARROW */
|
||||
};
|
||||
|
||||
#else
|
||||
/* UK keyboard */
|
||||
#define KEYMAP_SIZE (152)
|
||||
const KEYMAP keymap[KEYMAP_SIZE] = {
|
||||
{0, 0}, /* NUL */
|
||||
{0, 0}, /* SOH */
|
||||
{0, 0}, /* STX */
|
||||
{0, 0}, /* ETX */
|
||||
{0, 0}, /* EOT */
|
||||
{0, 0}, /* ENQ */
|
||||
{0, 0}, /* ACK */
|
||||
{0, 0}, /* BEL */
|
||||
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
|
||||
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
|
||||
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
|
||||
{0, 0}, /* VT */
|
||||
{0, 0}, /* FF */
|
||||
{0, 0}, /* CR */
|
||||
{0, 0}, /* SO */
|
||||
{0, 0}, /* SI */
|
||||
{0, 0}, /* DEL */
|
||||
{0, 0}, /* DC1 */
|
||||
{0, 0}, /* DC2 */
|
||||
{0, 0}, /* DC3 */
|
||||
{0, 0}, /* DC4 */
|
||||
{0, 0}, /* NAK */
|
||||
{0, 0}, /* SYN */
|
||||
{0, 0}, /* ETB */
|
||||
{0, 0}, /* CAN */
|
||||
{0, 0}, /* EM */
|
||||
{0, 0}, /* SUB */
|
||||
{0, 0}, /* ESC */
|
||||
{0, 0}, /* FS */
|
||||
{0, 0}, /* GS */
|
||||
{0, 0}, /* RS */
|
||||
{0, 0}, /* US */
|
||||
{0x2c, 0}, /* */
|
||||
{0x1e, KEY_SHIFT}, /* ! */
|
||||
{0x1f, KEY_SHIFT}, /* " */
|
||||
{0x32, 0}, /* # */
|
||||
{0x21, KEY_SHIFT}, /* $ */
|
||||
{0x22, KEY_SHIFT}, /* % */
|
||||
{0x24, KEY_SHIFT}, /* & */
|
||||
{0x34, 0}, /* ' */
|
||||
{0x26, KEY_SHIFT}, /* ( */
|
||||
{0x27, KEY_SHIFT}, /* ) */
|
||||
{0x25, KEY_SHIFT}, /* * */
|
||||
{0x2e, KEY_SHIFT}, /* + */
|
||||
{0x36, 0}, /* , */
|
||||
{0x2d, 0}, /* - */
|
||||
{0x37, 0}, /* . */
|
||||
{0x38, 0}, /* / */
|
||||
{0x27, 0}, /* 0 */
|
||||
{0x1e, 0}, /* 1 */
|
||||
{0x1f, 0}, /* 2 */
|
||||
{0x20, 0}, /* 3 */
|
||||
{0x21, 0}, /* 4 */
|
||||
{0x22, 0}, /* 5 */
|
||||
{0x23, 0}, /* 6 */
|
||||
{0x24, 0}, /* 7 */
|
||||
{0x25, 0}, /* 8 */
|
||||
{0x26, 0}, /* 9 */
|
||||
{0x33, KEY_SHIFT}, /* : */
|
||||
{0x33, 0}, /* ; */
|
||||
{0x36, KEY_SHIFT}, /* < */
|
||||
{0x2e, 0}, /* = */
|
||||
{0x37, KEY_SHIFT}, /* > */
|
||||
{0x38, KEY_SHIFT}, /* ? */
|
||||
{0x34, KEY_SHIFT}, /* @ */
|
||||
{0x04, KEY_SHIFT}, /* A */
|
||||
{0x05, KEY_SHIFT}, /* B */
|
||||
{0x06, KEY_SHIFT}, /* C */
|
||||
{0x07, KEY_SHIFT}, /* D */
|
||||
{0x08, KEY_SHIFT}, /* E */
|
||||
{0x09, KEY_SHIFT}, /* F */
|
||||
{0x0a, KEY_SHIFT}, /* G */
|
||||
{0x0b, KEY_SHIFT}, /* H */
|
||||
{0x0c, KEY_SHIFT}, /* I */
|
||||
{0x0d, KEY_SHIFT}, /* J */
|
||||
{0x0e, KEY_SHIFT}, /* K */
|
||||
{0x0f, KEY_SHIFT}, /* L */
|
||||
{0x10, KEY_SHIFT}, /* M */
|
||||
{0x11, KEY_SHIFT}, /* N */
|
||||
{0x12, KEY_SHIFT}, /* O */
|
||||
{0x13, KEY_SHIFT}, /* P */
|
||||
{0x14, KEY_SHIFT}, /* Q */
|
||||
{0x15, KEY_SHIFT}, /* R */
|
||||
{0x16, KEY_SHIFT}, /* S */
|
||||
{0x17, KEY_SHIFT}, /* T */
|
||||
{0x18, KEY_SHIFT}, /* U */
|
||||
{0x19, KEY_SHIFT}, /* V */
|
||||
{0x1a, KEY_SHIFT}, /* W */
|
||||
{0x1b, KEY_SHIFT}, /* X */
|
||||
{0x1c, KEY_SHIFT}, /* Y */
|
||||
{0x1d, KEY_SHIFT}, /* Z */
|
||||
{0x2f, 0}, /* [ */
|
||||
{0x64, 0}, /* \ */
|
||||
{0x30, 0}, /* ] */
|
||||
{0x23, KEY_SHIFT}, /* ^ */
|
||||
{0x2d, KEY_SHIFT}, /* _ */
|
||||
{0x35, 0}, /* ` */
|
||||
{0x04, 0}, /* a */
|
||||
{0x05, 0}, /* b */
|
||||
{0x06, 0}, /* c */
|
||||
{0x07, 0}, /* d */
|
||||
{0x08, 0}, /* e */
|
||||
{0x09, 0}, /* f */
|
||||
{0x0a, 0}, /* g */
|
||||
{0x0b, 0}, /* h */
|
||||
{0x0c, 0}, /* i */
|
||||
{0x0d, 0}, /* j */
|
||||
{0x0e, 0}, /* k */
|
||||
{0x0f, 0}, /* l */
|
||||
{0x10, 0}, /* m */
|
||||
{0x11, 0}, /* n */
|
||||
{0x12, 0}, /* o */
|
||||
{0x13, 0}, /* p */
|
||||
{0x14, 0}, /* q */
|
||||
{0x15, 0}, /* r */
|
||||
{0x16, 0}, /* s */
|
||||
{0x17, 0}, /* t */
|
||||
{0x18, 0}, /* u */
|
||||
{0x19, 0}, /* v */
|
||||
{0x1a, 0}, /* w */
|
||||
{0x1b, 0}, /* x */
|
||||
{0x1c, 0}, /* y */
|
||||
{0x1d, 0}, /* z */
|
||||
{0x2f, KEY_SHIFT}, /* { */
|
||||
{0x64, KEY_SHIFT}, /* | */
|
||||
{0x30, KEY_SHIFT}, /* } */
|
||||
{0x32, KEY_SHIFT}, /* ~ */
|
||||
{0,0}, /* DEL */
|
||||
|
||||
{0x3a, 0}, /* F1 */
|
||||
{0x3b, 0}, /* F2 */
|
||||
{0x3c, 0}, /* F3 */
|
||||
{0x3d, 0}, /* F4 */
|
||||
{0x3e, 0}, /* F5 */
|
||||
{0x3f, 0}, /* F6 */
|
||||
{0x40, 0}, /* F7 */
|
||||
{0x41, 0}, /* F8 */
|
||||
{0x42, 0}, /* F9 */
|
||||
{0x43, 0}, /* F10 */
|
||||
{0x44, 0}, /* F11 */
|
||||
{0x45, 0}, /* F12 */
|
||||
|
||||
{0x46, 0}, /* PRINT_SCREEN */
|
||||
{0x47, 0}, /* SCROLL_LOCK */
|
||||
{0x39, 0}, /* CAPS_LOCK */
|
||||
{0x53, 0}, /* NUM_LOCK */
|
||||
{0x49, 0}, /* INSERT */
|
||||
{0x4a, 0}, /* HOME */
|
||||
{0x4b, 0}, /* PAGE_UP */
|
||||
{0x4e, 0}, /* PAGE_DOWN */
|
||||
|
||||
{0x4f, 0}, /* RIGHT_ARROW */
|
||||
{0x50, 0}, /* LEFT_ARROW */
|
||||
{0x51, 0}, /* DOWN_ARROW */
|
||||
{0x52, 0}, /* UP_ARROW */
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
96
libraries/BLE/src/HIDTypes.h
Normal file
96
libraries/BLE/src/HIDTypes.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* Copyright (c) 2010-2011 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef USBCLASS_HID_TYPES
|
||||
#define USBCLASS_HID_TYPES
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* */
|
||||
#define HID_VERSION_1_11 (0x0111)
|
||||
|
||||
/* HID Class */
|
||||
#define HID_CLASS (3)
|
||||
#define HID_SUBCLASS_NONE (0)
|
||||
#define HID_PROTOCOL_NONE (0)
|
||||
|
||||
/* Descriptors */
|
||||
#define HID_DESCRIPTOR (33)
|
||||
#define HID_DESCRIPTOR_LENGTH (0x09)
|
||||
#define REPORT_DESCRIPTOR (34)
|
||||
|
||||
/* Class requests */
|
||||
#define GET_REPORT (0x1)
|
||||
#define GET_IDLE (0x2)
|
||||
#define SET_REPORT (0x9)
|
||||
#define SET_IDLE (0xa)
|
||||
|
||||
/* HID Class Report Descriptor */
|
||||
/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */
|
||||
/* of data as per HID Class standard */
|
||||
|
||||
/* Main items */
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HIDINPUT(size) (0x80 | size)
|
||||
#define HIDOUTPUT(size) (0x90 | size)
|
||||
#else
|
||||
#define INPUT(size) (0x80 | size)
|
||||
#define OUTPUT(size) (0x90 | size)
|
||||
#endif
|
||||
#define FEATURE(size) (0xb0 | size)
|
||||
#define COLLECTION(size) (0xa0 | size)
|
||||
#define END_COLLECTION(size) (0xc0 | size)
|
||||
|
||||
/* Global items */
|
||||
#define USAGE_PAGE(size) (0x04 | size)
|
||||
#define LOGICAL_MINIMUM(size) (0x14 | size)
|
||||
#define LOGICAL_MAXIMUM(size) (0x24 | size)
|
||||
#define PHYSICAL_MINIMUM(size) (0x34 | size)
|
||||
#define PHYSICAL_MAXIMUM(size) (0x44 | size)
|
||||
#define UNIT_EXPONENT(size) (0x54 | size)
|
||||
#define UNIT(size) (0x64 | size)
|
||||
#define REPORT_SIZE(size) (0x74 | size) //bits
|
||||
#define REPORT_ID(size) (0x84 | size)
|
||||
#define REPORT_COUNT(size) (0x94 | size) //bytes
|
||||
#define PUSH(size) (0xa4 | size)
|
||||
#define POP(size) (0xb4 | size)
|
||||
|
||||
/* Local items */
|
||||
#define USAGE(size) (0x08 | size)
|
||||
#define USAGE_MINIMUM(size) (0x18 | size)
|
||||
#define USAGE_MAXIMUM(size) (0x28 | size)
|
||||
#define DESIGNATOR_INDEX(size) (0x38 | size)
|
||||
#define DESIGNATOR_MINIMUM(size) (0x48 | size)
|
||||
#define DESIGNATOR_MAXIMUM(size) (0x58 | size)
|
||||
#define STRING_INDEX(size) (0x78 | size)
|
||||
#define STRING_MINIMUM(size) (0x88 | size)
|
||||
#define STRING_MAXIMUM(size) (0x98 | size)
|
||||
#define DELIMITER(size) (0xa8 | size)
|
||||
|
||||
/* HID Report */
|
||||
/* Where report IDs are used the first byte of 'data' will be the */
|
||||
/* report ID and 'length' will include this report ID byte. */
|
||||
|
||||
#define MAX_HID_REPORT_SIZE (64)
|
||||
|
||||
typedef struct {
|
||||
uint32_t length;
|
||||
uint8_t data[MAX_HID_REPORT_SIZE];
|
||||
} HID_REPORT;
|
||||
|
||||
#endif
|
@ -10,92 +10,14 @@
|
||||
#define CAMERA_MODEL_WROVER_KIT
|
||||
//#define CAMERA_MODEL_ESP_EYE
|
||||
//#define CAMERA_MODEL_M5STACK_PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_WIDE
|
||||
//#define CAMERA_MODEL_AI_THINKER
|
||||
|
||||
#include "camera_pins.h"
|
||||
|
||||
const char* ssid = "*********";
|
||||
const char* password = "*********";
|
||||
|
||||
|
||||
#if defined(CAMERA_MODEL_WROVER_KIT)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 21
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 19
|
||||
#define Y4_GPIO_NUM 18
|
||||
#define Y3_GPIO_NUM 5
|
||||
#define Y2_GPIO_NUM 4
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#elif defined(CAMERA_MODEL_ESP_EYE)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 4
|
||||
#define SIOD_GPIO_NUM 18
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 36
|
||||
#define Y8_GPIO_NUM 37
|
||||
#define Y7_GPIO_NUM 38
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 35
|
||||
#define Y4_GPIO_NUM 14
|
||||
#define Y3_GPIO_NUM 13
|
||||
#define Y2_GPIO_NUM 34
|
||||
#define VSYNC_GPIO_NUM 5
|
||||
#define HREF_GPIO_NUM 27
|
||||
#define PCLK_GPIO_NUM 25
|
||||
|
||||
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM 15
|
||||
#define XCLK_GPIO_NUM 27
|
||||
#define SIOD_GPIO_NUM 25
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 19
|
||||
#define Y8_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 5
|
||||
#define Y4_GPIO_NUM 34
|
||||
#define Y3_GPIO_NUM 35
|
||||
#define Y2_GPIO_NUM 32
|
||||
#define VSYNC_GPIO_NUM 22
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 0
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 21
|
||||
#define Y4_GPIO_NUM 19
|
||||
#define Y3_GPIO_NUM 18
|
||||
#define Y2_GPIO_NUM 5
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#else
|
||||
#error "Camera model not selected"
|
||||
#endif
|
||||
|
||||
void startCameraServer();
|
||||
|
||||
void setup() {
|
||||
@ -147,10 +69,21 @@ void setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
//drop down frame size for higher initial frame rate
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
//initial sensors are flipped vertically and colors are a bit saturated
|
||||
if (s->id.PID == OV3660_PID) {
|
||||
s->set_vflip(s, 1);//flip it back
|
||||
s->set_brightness(s, 1);//up the blightness just a bit
|
||||
s->set_saturation(s, -2);//lower the saturation
|
||||
}
|
||||
//drop down frame size for higher initial frame rate
|
||||
s->set_framesize(s, FRAMESIZE_QVGA);
|
||||
|
||||
#if defined(CAMERA_MODEL_M5STACK_WIDE)
|
||||
s->set_vflip(s, 1);
|
||||
s->set_hmirror(s, 1);
|
||||
#endif
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
|
@ -544,6 +544,7 @@ static esp_err_t status_handler(httpd_req_t *req){
|
||||
p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
|
||||
p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
|
||||
p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
|
||||
p+=sprintf(p, "\"sharpness\":%d,", s->status.sharpness);
|
||||
p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
|
||||
p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
|
||||
p+=sprintf(p, "\"awb\":%u,", s->status.awb);
|
||||
@ -576,7 +577,11 @@ static esp_err_t status_handler(httpd_req_t *req){
|
||||
static esp_err_t index_handler(httpd_req_t *req){
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
|
||||
return httpd_resp_send(req, (const char *)index_html_gz, index_html_gz_len);
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s->id.PID == OV3660_PID) {
|
||||
return httpd_resp_send(req, (const char *)index_ov3660_html_gz, index_ov3660_html_gz_len);
|
||||
}
|
||||
return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len);
|
||||
}
|
||||
|
||||
void startCameraServer(){
|
||||
|
@ -1,233 +1,558 @@
|
||||
//File: index.html.gz, Size: 3663
|
||||
#define index_html_gz_len 3663
|
||||
const uint8_t index_html_gz[] = {
|
||||
0x1F, 0x8B, 0x08, 0x08, 0x60, 0x15, 0x36, 0x5C, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E,
|
||||
0x68, 0x74, 0x6D, 0x6C, 0x00, 0xDD, 0x5C, 0xEB, 0x72, 0x9B, 0xC8, 0x12, 0xFE, 0x7F, 0x9E, 0x02,
|
||||
0x93, 0xDD, 0x08, 0x6A, 0x91, 0x2C, 0xC9, 0x8A, 0xE3, 0x20, 0x0B, 0x1F, 0x5B, 0x76, 0x92, 0xAD,
|
||||
0xCA, 0x6D, 0xE3, 0x3D, 0xBB, 0x5B, 0xB5, 0xB5, 0x95, 0x8C, 0x60, 0x90, 0x26, 0x46, 0x8C, 0x02,
|
||||
0x83, 0x2E, 0xD1, 0xF2, 0x1C, 0xE7, 0x81, 0xCE, 0x8B, 0x9D, 0x9E, 0x19, 0x40, 0xA0, 0x8B, 0x65,
|
||||
0x49, 0x89, 0x94, 0x5A, 0xBB, 0xCA, 0x1A, 0xA0, 0xBB, 0xA7, 0xBB, 0xBF, 0xBE, 0x0C, 0x88, 0xF1,
|
||||
0xF9, 0x91, 0x43, 0x6D, 0x36, 0x19, 0x60, 0xA5, 0xC7, 0xFA, 0x9E, 0xF5, 0xAF, 0x73, 0xF9, 0xA1,
|
||||
0xC0, 0xCF, 0x79, 0x0F, 0x23, 0x47, 0x0E, 0xC5, 0x61, 0x1F, 0x33, 0xA4, 0xD8, 0x3D, 0x14, 0x84,
|
||||
0x98, 0xB5, 0xD4, 0x88, 0xB9, 0xE5, 0x33, 0x75, 0xFE, 0xB2, 0x8F, 0xFA, 0xB8, 0xA5, 0x0E, 0x09,
|
||||
0x1E, 0x0D, 0x68, 0xC0, 0x54, 0xC5, 0xA6, 0x3E, 0xC3, 0x3E, 0x90, 0x8F, 0x88, 0xC3, 0x7A, 0x2D,
|
||||
0x07, 0x0F, 0x89, 0x8D, 0xCB, 0xE2, 0xC0, 0x20, 0x3E, 0x61, 0x04, 0x79, 0xE5, 0xD0, 0x46, 0x1E,
|
||||
0x6E, 0xD5, 0xF2, 0xB2, 0x18, 0x61, 0x1E, 0xB6, 0x6E, 0x6E, 0xDF, 0x9D, 0xD4, 0x95, 0xB7, 0xBF,
|
||||
0xD5, 0x1B, 0xA7, 0xD5, 0xF3, 0x63, 0x79, 0x6E, 0x46, 0x13, 0xB2, 0x09, 0x3F, 0xEE, 0x50, 0x67,
|
||||
0x32, 0x75, 0x61, 0x9A, 0xB2, 0x8B, 0xFA, 0xC4, 0x9B, 0x98, 0x97, 0x01, 0x08, 0x35, 0x5E, 0x62,
|
||||
0x6F, 0x88, 0x19, 0xB1, 0x91, 0x11, 0x22, 0x3F, 0x2C, 0x87, 0x38, 0x20, 0x6E, 0xB3, 0x83, 0xEC,
|
||||
0xBB, 0x6E, 0x40, 0x23, 0xDF, 0x31, 0x1F, 0xD5, 0xCE, 0xF8, 0x6F, 0xD3, 0xA6, 0x1E, 0x0D, 0xCC,
|
||||
0x47, 0x37, 0xCF, 0xF9, 0x6F, 0x53, 0xC8, 0x09, 0xC9, 0x17, 0x6C, 0xD6, 0x4E, 0x07, 0xE3, 0xB8,
|
||||
0x57, 0x9F, 0xE6, 0xCE, 0x9C, 0xC1, 0x99, 0x10, 0xDB, 0x8C, 0x50, 0xBF, 0xD2, 0x47, 0xC4, 0x9F,
|
||||
0x3A, 0x24, 0x1C, 0x78, 0x68, 0x62, 0xBA, 0x1E, 0x1E, 0xC7, 0x8F, 0xFA, 0xD8, 0x8F, 0x8C, 0xC2,
|
||||
0x75, 0x7E, 0xBE, 0xEC, 0x90, 0x40, 0x9E, 0x33, 0x61, 0xAA, 0xA8, 0xEF, 0x4B, 0xC2, 0x8C, 0xD7,
|
||||
0xA7, 0x3E, 0x6E, 0x0A, 0xC2, 0x51, 0x80, 0x06, 0x70, 0xC8, 0x3F, 0x9A, 0x7D, 0xE2, 0x4B, 0x27,
|
||||
0x99, 0x27, 0x8D, 0xEA, 0x60, 0x5C, 0x50, 0xFC, 0xE4, 0x94, 0xFF, 0x36, 0x07, 0xC8, 0x71, 0x88,
|
||||
0xDF, 0x35, 0xCF, 0xF8, 0x65, 0x1A, 0x38, 0x38, 0x28, 0x07, 0xC8, 0x21, 0x51, 0x68, 0x36, 0xE0,
|
||||
0x4C, 0x1F, 0x05, 0x5D, 0x90, 0xC1, 0xE8, 0xC0, 0x2C, 0xD7, 0xAA, 0xB3, 0x13, 0x01, 0xE9, 0xF6,
|
||||
0x98, 0xC9, 0xCF, 0xC4, 0x8F, 0x12, 0x6C, 0x0A, 0x66, 0xE4, 0x54, 0x11, 0x8A, 0x20, 0x8F, 0x74,
|
||||
0xFD, 0x32, 0x61, 0xB8, 0x1F, 0x9A, 0x21, 0x0B, 0x30, 0xB3, 0x7B, 0xB1, 0x4B, 0xBA, 0x51, 0x80,
|
||||
0xA7, 0xA9, 0x02, 0xD5, 0x44, 0x36, 0x0C, 0xCA, 0x23, 0xDC, 0xB9, 0x23, 0xAC, 0x9C, 0x4C, 0xD6,
|
||||
0xC1, 0x2E, 0x0D, 0x70, 0x46, 0x50, 0xEE, 0x78, 0xD4, 0xBE, 0x2B, 0x87, 0x0C, 0x05, 0x6C, 0x91,
|
||||
0x18, 0xB9, 0x0C, 0x07, 0xF3, 0xB4, 0x18, 0x0C, 0x5E, 0xA0, 0x4C, 0x05, 0x24, 0x87, 0xC4, 0xF7,
|
||||
0x88, 0x8F, 0x57, 0x89, 0x95, 0x12, 0x8A, 0xA4, 0xE2, 0x5C, 0x62, 0x86, 0x42, 0xFA, 0xDD, 0xCC,
|
||||
0x03, 0x62, 0xD2, 0xA6, 0x74, 0x7C, 0xAD, 0x5A, 0xFD, 0xB1, 0xD9, 0xC3, 0xC2, 0x5F, 0x28, 0x62,
|
||||
0xF4, 0x7E, 0x27, 0xF3, 0xD8, 0xF8, 0x77, 0x1F, 0x3B, 0x04, 0x29, 0xDA, 0x0C, 0x3C, 0xE5, 0xAC,
|
||||
0x0A, 0x9E, 0xD6, 0x15, 0xE4, 0x3B, 0x8A, 0x46, 0x03, 0x02, 0xDE, 0x46, 0x22, 0x14, 0x3C, 0x38,
|
||||
0x03, 0x61, 0x3F, 0xC0, 0xFA, 0x74, 0x1D, 0x0C, 0x49, 0x44, 0xAC, 0x06, 0x62, 0x89, 0x05, 0x7D,
|
||||
0x34, 0x2E, 0xE7, 0xAC, 0xE0, 0x87, 0x89, 0x25, 0x90, 0x6A, 0xB6, 0x06, 0x27, 0x87, 0x3D, 0xA5,
|
||||
0xAC, 0xF0, 0xD0, 0xD2, 0x13, 0x73, 0x85, 0x89, 0x39, 0x73, 0xFF, 0x29, 0x28, 0xA7, 0x19, 0xFB,
|
||||
0xA8, 0x13, 0x31, 0x46, 0xFD, 0x70, 0x8D, 0x9B, 0x3F, 0x45, 0x21, 0x23, 0xEE, 0xA4, 0x9C, 0x80,
|
||||
0x62, 0x86, 0x03, 0x04, 0xF5, 0xAA, 0x83, 0xD9, 0x08, 0x63, 0x48, 0x5D, 0x1F, 0x0D, 0x01, 0xEE,
|
||||
0x6E, 0xD7, 0xC3, 0x53, 0x3B, 0x0A, 0x42, 0xA8, 0x1C, 0x03, 0x4A, 0x80, 0x32, 0x68, 0x16, 0x00,
|
||||
0xC8, 0x13, 0x96, 0xED, 0xCE, 0x94, 0x46, 0x8C, 0xAB, 0x04, 0x2A, 0x52, 0x90, 0x47, 0xD8, 0x04,
|
||||
0x46, 0xD2, 0xED, 0xD5, 0xD4, 0xE7, 0xD5, 0x39, 0x1E, 0xD3, 0xEE, 0x61, 0xFB, 0x0E, 0x3B, 0x3F,
|
||||
0x15, 0xCB, 0x85, 0x28, 0x35, 0x15, 0xE2, 0x0F, 0x22, 0x56, 0xE6, 0x05, 0x61, 0xB0, 0xC6, 0x1E,
|
||||
0xE1, 0x89, 0x64, 0x8A, 0x7A, 0x3D, 0x8B, 0x59, 0xF3, 0xC9, 0x60, 0xAC, 0x54, 0x0B, 0x82, 0x2C,
|
||||
0x0F, 0x75, 0xB0, 0x97, 0x89, 0x4B, 0x9C, 0x28, 0xE3, 0x29, 0x09, 0x82, 0x5C, 0xF5, 0xC8, 0x55,
|
||||
0xA8, 0xC6, 0xD3, 0x1F, 0x0B, 0x82, 0x14, 0x31, 0x36, 0x0A, 0xA7, 0x42, 0xEC, 0x01, 0x0C, 0xB2,
|
||||
0x20, 0xC2, 0x99, 0x91, 0x59, 0x8B, 0x2B, 0x01, 0xF2, 0xBB, 0x18, 0x00, 0x1C, 0x1B, 0xE9, 0x30,
|
||||
0x57, 0x52, 0x97, 0x4D, 0x6F, 0x56, 0x15, 0x50, 0x3B, 0x96, 0x40, 0x2E, 0x44, 0x7C, 0x6A, 0x56,
|
||||
0x8E, 0xBA, 0x56, 0xCF, 0x6A, 0x23, 0x38, 0xBA, 0xE0, 0x0A, 0x5E, 0x35, 0xE7, 0x10, 0x4C, 0x3A,
|
||||
0x81, 0xEB, 0x16, 0xFB, 0x84, 0xEB, 0x9E, 0x54, 0x4F, 0x1A, 0x73, 0xD9, 0xCF, 0xE7, 0x29, 0xF6,
|
||||
0x8A, 0x66, 0x86, 0x71, 0xA2, 0xA0, 0xD9, 0xA3, 0x43, 0x1C, 0x4C, 0x8B, 0xA2, 0x1A, 0xCF, 0x1A,
|
||||
0x4E, 0x7A, 0x1D, 0x41, 0x5C, 0x0E, 0x71, 0x91, 0xA0, 0x5E, 0xB3, 0xEB, 0xB5, 0x84, 0xA0, 0x02,
|
||||
0x16, 0xA2, 0x8E, 0x87, 0x9D, 0x34, 0xD4, 0x1C, 0xEC, 0xA2, 0xC8, 0x63, 0x05, 0xED, 0x50, 0x95,
|
||||
0xFF, 0xC6, 0xC2, 0xD7, 0x7F, 0xF2, 0x36, 0xDE, 0x12, 0xBE, 0xFC, 0x6B, 0x9A, 0x26, 0x08, 0x1A,
|
||||
0x0C, 0x30, 0x82, 0x73, 0x36, 0x96, 0xAD, 0x66, 0xB1, 0xB8, 0x89, 0xB0, 0x58, 0xD2, 0x60, 0xE6,
|
||||
0xDC, 0x93, 0xA6, 0xFF, 0xE2, 0x5C, 0xA6, 0x4B, 0xED, 0x28, 0x9C, 0x05, 0xF9, 0x12, 0x0A, 0x33,
|
||||
0x55, 0x27, 0xF4, 0x88, 0x70, 0x63, 0xE4, 0xFB, 0xDC, 0xB6, 0x32, 0x0B, 0x60, 0xE2, 0xE9, 0x12,
|
||||
0xA5, 0x16, 0xF1, 0xC9, 0xAB, 0x98, 0xB4, 0xEB, 0x22, 0x28, 0xD5, 0x0C, 0x6B, 0x25, 0xA4, 0x30,
|
||||
0x8F, 0x92, 0x90, 0x3D, 0x40, 0x1F, 0xD6, 0x8B, 0xFA, 0x9D, 0x69, 0xC2, 0x5E, 0x83, 0xDC, 0x90,
|
||||
0x02, 0x82, 0x6E, 0x07, 0x69, 0x55, 0xA3, 0x6A, 0x9C, 0xC0, 0x1F, 0xBD, 0xE0, 0x30, 0xA9, 0x72,
|
||||
0xBD, 0xBE, 0xD0, 0x7D, 0x9F, 0xCC, 0xF7, 0xEB, 0x24, 0x80, 0xE6, 0xAC, 0x59, 0x85, 0x4F, 0xA1,
|
||||
0x71, 0xD7, 0x2A, 0x3C, 0xE0, 0x57, 0x38, 0x7C, 0x9D, 0x53, 0x17, 0xFD, 0xB5, 0xD4, 0x11, 0x7D,
|
||||
0xFA, 0xA5, 0x2C, 0xF3, 0xEF, 0x60, 0x58, 0xE4, 0x54, 0xD8, 0x37, 0x0E, 0xCB, 0xF5, 0x09, 0xB7,
|
||||
0xF4, 0x45, 0x55, 0x49, 0xED, 0x2E, 0xCB, 0x6A, 0x02, 0x62, 0x7C, 0x68, 0x21, 0x01, 0xB4, 0x92,
|
||||
0xE6, 0xC2, 0x99, 0x55, 0x73, 0xBB, 0xC4, 0xF3, 0xCA, 0x1E, 0x1D, 0xCD, 0x55, 0x8F, 0x82, 0x9F,
|
||||
0xE7, 0xFD, 0x3A, 0xEF, 0xFE, 0x7B, 0x65, 0x47, 0x10, 0x73, 0xDF, 0x40, 0xF6, 0xFE, 0x93, 0x68,
|
||||
0x06, 0xCA, 0x3D, 0x49, 0xB2, 0xCE, 0xA3, 0x0F, 0x60, 0x5D, 0x74, 0x98, 0xAC, 0x91, 0x71, 0x25,
|
||||
0x1C, 0x11, 0x58, 0x89, 0xCD, 0x35, 0xA3, 0x01, 0x0D, 0x89, 0x58, 0xE6, 0x05, 0xD8, 0x43, 0xBC,
|
||||
0xC8, 0x2F, 0xB6, 0xE1, 0xB9, 0xE6, 0x91, 0xBB, 0x94, 0xCA, 0x94, 0x6D, 0xF4, 0x61, 0x4B, 0x87,
|
||||
0x8A, 0xAC, 0x00, 0x49, 0xBC, 0x0A, 0xE7, 0x15, 0x8A, 0x7B, 0xC1, 0xB7, 0xF5, 0x7B, 0x63, 0x38,
|
||||
0x09, 0xDC, 0x6E, 0x80, 0x27, 0xA9, 0x58, 0x23, 0xF9, 0x34, 0xE5, 0x4A, 0x6F, 0x79, 0x8F, 0x16,
|
||||
0x71, 0x2D, 0xAD, 0xAE, 0x34, 0xC2, 0x78, 0x8E, 0x65, 0xD1, 0x23, 0xE9, 0x02, 0x4B, 0x55, 0x17,
|
||||
0xA0, 0xCF, 0x92, 0x4D, 0xB8, 0x26, 0xC9, 0x41, 0x3E, 0xF4, 0xB0, 0xCB, 0xC4, 0xC2, 0x9B, 0x57,
|
||||
0xC7, 0x93, 0x42, 0x84, 0x94, 0x67, 0xDD, 0x5B, 0xE2, 0x99, 0xAD, 0x9F, 0x52, 0xDF, 0x2C, 0xA3,
|
||||
0xE5, 0x31, 0xB5, 0x9C, 0x3C, 0x55, 0x3C, 0x2D, 0xB1, 0xC2, 0x3C, 0x38, 0xD3, 0x97, 0x09, 0x0C,
|
||||
0x46, 0xE0, 0x3F, 0xB4, 0xFA, 0x29, 0x5F, 0x3F, 0xAF, 0xBE, 0x14, 0x27, 0xCB, 0x9E, 0x85, 0x94,
|
||||
0x48, 0x5B, 0x6C, 0x2E, 0x0A, 0x1A, 0x73, 0x98, 0xCD, 0x70, 0x5F, 0x58, 0x79, 0xC0, 0x6A, 0xAB,
|
||||
0x8F, 0xA0, 0x58, 0x72, 0x17, 0xC2, 0x6D, 0x26, 0xD8, 0xB6, 0xE8, 0xDE, 0xD9, 0xF2, 0xAC, 0x76,
|
||||
0xCA, 0x6F, 0xF6, 0x2A, 0xB6, 0x47, 0xC3, 0x1C, 0x0E, 0xA8, 0x03, 0x9A, 0x44, 0x0C, 0x37, 0xE5,
|
||||
0x92, 0xEE, 0x49, 0xE2, 0xD4, 0x27, 0xCB, 0xD3, 0x2E, 0x87, 0x41, 0x1E, 0x9A, 0xA2, 0x66, 0x35,
|
||||
0x7E, 0xAF, 0x93, 0x5F, 0x45, 0x31, 0x3C, 0x86, 0xFE, 0xC6, 0xEF, 0x5B, 0x4C, 0x1B, 0x8B, 0x30,
|
||||
0xCB, 0xA7, 0x41, 0x6D, 0x71, 0x09, 0x16, 0x57, 0x7A, 0xC4, 0x71, 0xB0, 0x5F, 0xB8, 0x39, 0x8E,
|
||||
0x67, 0x77, 0xFC, 0xC7, 0xC9, 0x2D, 0xBF, 0x3C, 0x98, 0x3D, 0x9D, 0x38, 0xE7, 0xCF, 0x00, 0xF2,
|
||||
0x4F, 0x06, 0xE4, 0x92, 0x5F, 0xB1, 0x3D, 0x14, 0x86, 0x2D, 0x95, 0xDF, 0x8B, 0xE7, 0x1E, 0x2E,
|
||||
0x08, 0x12, 0x87, 0x0C, 0x15, 0xE2, 0xB4, 0x54, 0x8F, 0x76, 0xE9, 0xDC, 0x35, 0x71, 0x5D, 0x2C,
|
||||
0x86, 0x15, 0x40, 0xB5, 0xA5, 0x16, 0x96, 0xE5, 0xAA, 0xE0, 0x9A, 0x9D, 0x52, 0xAD, 0xC7, 0x8F,
|
||||
0x9E, 0x3D, 0x7D, 0x7A, 0xDA, 0x7C, 0xEC, 0x77, 0xC2, 0x41, 0xF2, 0xF7, 0x57, 0x71, 0x09, 0x16,
|
||||
0xBD, 0x8C, 0xC1, 0x42, 0x34, 0x3C, 0x3F, 0x16, 0xD2, 0xE6, 0x34, 0x38, 0x06, 0x15, 0x56, 0x28,
|
||||
0x95, 0xE4, 0xC6, 0x32, 0xBD, 0x52, 0x92, 0x10, 0x82, 0xB4, 0x83, 0x82, 0x25, 0x24, 0x82, 0x4C,
|
||||
0xC4, 0xB4, 0x22, 0x4A, 0x9A, 0x2A, 0x22, 0xBB, 0x43, 0xC7, 0xF3, 0xAA, 0x0B, 0x6B, 0x92, 0xB0,
|
||||
0x4F, 0xA8, 0xB0, 0xB3, 0x4A, 0x20, 0xB0, 0x09, 0x76, 0x7E, 0x33, 0xB2, 0x82, 0x26, 0xD3, 0x2F,
|
||||
0x71, 0x7B, 0x6E, 0xFD, 0x2F, 0xA7, 0x76, 0x03, 0xD4, 0xC7, 0x3C, 0xDA, 0x93, 0x93, 0xAB, 0xC5,
|
||||
0xCC, 0x43, 0x90, 0x71, 0xAA, 0xD6, 0x7B, 0x2C, 0x02, 0x17, 0xE0, 0x5D, 0xEA, 0xD6, 0x05, 0x29,
|
||||
0x32, 0x05, 0x8B, 0xF3, 0xAB, 0xA9, 0x8A, 0xC9, 0x8A, 0xBA, 0x8C, 0x44, 0xBC, 0xAC, 0x51, 0x48,
|
||||
0x88, 0xA3, 0x03, 0x11, 0x59, 0x43, 0xE4, 0x45, 0xE0, 0xDA, 0x5A, 0x55, 0xB5, 0xFE, 0xF3, 0xC7,
|
||||
0x8B, 0x4B, 0x0D, 0x92, 0xAC, 0x3A, 0xAE, 0xD5, 0xAB, 0x55, 0xFD, 0xFC, 0x58, 0x92, 0x6C, 0x2C,
|
||||
0xEB, 0x99, 0x6A, 0xDD, 0x0A, 0x51, 0xF5, 0x33, 0x10, 0x55, 0xAD, 0x37, 0xB6, 0x17, 0x75, 0xA6,
|
||||
0x5A, 0x42, 0x12, 0x08, 0x19, 0x3F, 0x3D, 0x3D, 0xDB, 0x5E, 0xD0, 0x53, 0xD0, 0xE9, 0x37, 0x90,
|
||||
0x74, 0x06, 0xD6, 0x9D, 0xEE, 0x62, 0xDC, 0xA9, 0x6A, 0x71, 0x39, 0xA7, 0x8D, 0xEA, 0xB8, 0x71,
|
||||
0xB6, 0x83, 0x9C, 0x27, 0x6A, 0x72, 0x2B, 0xC9, 0x43, 0x36, 0x1D, 0xA9, 0x56, 0xFB, 0xE7, 0xE7,
|
||||
0x5A, 0x03, 0x74, 0xAC, 0x3F, 0x3B, 0xDD, 0x5E, 0x76, 0x43, 0xB5, 0x7E, 0xE1, 0x4A, 0x9E, 0xD4,
|
||||
0x41, 0x50, 0x63, 0x07, 0x25, 0x4F, 0x54, 0xEB, 0xA5, 0x90, 0x04, 0x52, 0xC6, 0xB5, 0xA7, 0x3B,
|
||||
0xA8, 0x04, 0xE1, 0xF5, 0x8B, 0x90, 0x04, 0xF1, 0xC5, 0xC3, 0xEB, 0x81, 0x92, 0xA0, 0x50, 0x0A,
|
||||
0xD7, 0xDC, 0x93, 0xA7, 0x8B, 0xD5, 0xA7, 0x70, 0xF9, 0xBE, 0x34, 0xFE, 0x1C, 0x41, 0x4D, 0x67,
|
||||
0x93, 0x8D, 0x93, 0x38, 0xE1, 0x03, 0x93, 0xE4, 0xE0, 0x61, 0xF9, 0x9B, 0xD3, 0x24, 0x7B, 0x4A,
|
||||
0xA0, 0x5A, 0xB5, 0xEA, 0x1A, 0x0B, 0x04, 0x6F, 0xBE, 0x0A, 0x0A, 0xE6, 0x82, 0x01, 0xAA, 0x02,
|
||||
0xA2, 0x44, 0x0E, 0x2B, 0x7D, 0x34, 0x86, 0x18, 0x3D, 0x51, 0x73, 0x79, 0xBD, 0x55, 0x89, 0x58,
|
||||
0xA2, 0x2D, 0x1A, 0xAB, 0xD6, 0xE9, 0xC9, 0x3A, 0x7F, 0xEF, 0x00, 0x47, 0x47, 0x74, 0x70, 0x1F,
|
||||
0x87, 0xE1, 0xC6, 0x88, 0xCC, 0x58, 0x55, 0xEB, 0x2A, 0x1B, 0xEF, 0x82, 0x4B, 0xB9, 0xBE, 0x03,
|
||||
0x2E, 0x39, 0x75, 0x24, 0x34, 0xE5, 0x7A, 0x02, 0x4D, 0x5D, 0x9D, 0x65, 0xC4, 0xD7, 0x04, 0x66,
|
||||
0x9D, 0xB6, 0xBB, 0xE0, 0xC2, 0x9B, 0x78, 0x80, 0x42, 0xB6, 0x31, 0x2A, 0x29, 0x23, 0x94, 0xB5,
|
||||
0x64, 0x74, 0x30, 0x44, 0x32, 0x55, 0xFE, 0x01, 0x78, 0x84, 0x88, 0x45, 0x81, 0x78, 0xFA, 0xBE,
|
||||
0x31, 0x22, 0x33, 0x56, 0xE8, 0x87, 0xD9, 0xF8, 0x60, 0xA8, 0xE4, 0xD4, 0xF9, 0x27, 0xE0, 0x32,
|
||||
0xC0, 0x36, 0x41, 0xDE, 0x07, 0xEC, 0xBA, 0xD0, 0xB2, 0x36, 0xC7, 0xA6, 0xC0, 0x0E, 0xF8, 0xC8,
|
||||
0x63, 0xE5, 0x46, 0x1C, 0x6F, 0xBC, 0x46, 0x9C, 0x13, 0xF7, 0xB5, 0x16, 0x8A, 0xD5, 0xE5, 0xEB,
|
||||
0x96, 0x37, 0x34, 0xD3, 0x73, 0xCB, 0x15, 0x42, 0x0D, 0x84, 0xE0, 0xAE, 0xB8, 0xE7, 0xDB, 0x5A,
|
||||
0x46, 0x5D, 0xB5, 0x5E, 0x04, 0x68, 0x22, 0xBE, 0x86, 0xDD, 0x65, 0xD1, 0xF3, 0x1E, 0x3B, 0xCA,
|
||||
0xAF, 0x70, 0x23, 0xB7, 0xCB, 0x0A, 0xEC, 0x45, 0x80, 0xB1, 0xBF, 0x9B, 0x94, 0x27, 0xD0, 0xCC,
|
||||
0x60, 0xB0, 0x9B, 0x10, 0x58, 0xB0, 0xDE, 0xE2, 0x01, 0x41, 0xDF, 0xC3, 0x82, 0x0B, 0x8D, 0x3A,
|
||||
0x1B, 0xA7, 0x05, 0xF0, 0xA8, 0xD6, 0xE5, 0xEF, 0x57, 0x1B, 0x17, 0x29, 0xF9, 0xF0, 0xE9, 0x21,
|
||||
0x11, 0x2E, 0xAB, 0x53, 0xA2, 0xA0, 0xBA, 0x70, 0xB3, 0xB9, 0x3C, 0x73, 0x1E, 0x7A, 0xC3, 0xB9,
|
||||
0xC4, 0xAE, 0x54, 0x41, 0xF1, 0x7C, 0x46, 0xCD, 0x99, 0xF9, 0x30, 0x1B, 0xBF, 0x5D, 0x05, 0x03,
|
||||
0x25, 0x3E, 0x74, 0x11, 0xD9, 0xBC, 0xAF, 0xA4, 0x8C, 0x02, 0x29, 0xE5, 0x05, 0x8C, 0xF6, 0x05,
|
||||
0x97, 0x9C, 0xF6, 0x60, 0x98, 0x25, 0x56, 0x1F, 0x1A, 0x38, 0x50, 0xA4, 0x4F, 0x9D, 0xCD, 0x1F,
|
||||
0x47, 0x24, 0x7C, 0xAA, 0x05, 0xA8, 0xBD, 0x86, 0xC1, 0xC6, 0x5D, 0x26, 0x15, 0xF0, 0x8D, 0xDB,
|
||||
0xCB, 0x65, 0xC4, 0xE8, 0x2E, 0x9D, 0xE5, 0x36, 0xF2, 0xFD, 0xC9, 0x2E, 0x6D, 0xA5, 0xED, 0xD1,
|
||||
0xC8, 0xD9, 0x5E, 0x02, 0xF4, 0x94, 0xB7, 0xAE, 0x4B, 0xEC, 0xED, 0xBB, 0x12, 0x74, 0x94, 0x97,
|
||||
0xB4, 0xFF, 0x40, 0xFE, 0x6F, 0x5C, 0xC5, 0xB1, 0xBD, 0x79, 0x81, 0xC0, 0x36, 0xA0, 0x78, 0xD3,
|
||||
0x56, 0x6E, 0x6F, 0xDE, 0xDC, 0xBE, 0x7D, 0xBF, 0x9F, 0xEA, 0x00, 0x73, 0x1E, 0xA8, 0x30, 0x70,
|
||||
0x6B, 0x0F, 0x5D, 0x13, 0x40, 0x89, 0xFA, 0x36, 0x38, 0xD5, 0x25, 0x50, 0xD7, 0xB7, 0xEF, 0xF6,
|
||||
0x85, 0x52, 0xFD, 0x70, 0x30, 0xD5, 0xBF, 0x07, 0x9C, 0x3E, 0x78, 0x78, 0x88, 0xBD, 0x2D, 0xB0,
|
||||
0x92, 0x8C, 0x1C, 0x2F, 0xE5, 0x15, 0x1F, 0x1D, 0xEC, 0x46, 0x2E, 0x53, 0xE5, 0x1F, 0x70, 0x1B,
|
||||
0x07, 0x51, 0xF1, 0x41, 0x28, 0xBD, 0x4D, 0xF2, 0x48, 0x4E, 0xD5, 0xBA, 0x19, 0x0F, 0x68, 0x18,
|
||||
0x05, 0x0F, 0x6C, 0xA8, 0xCB, 0x11, 0xD9, 0xE5, 0xC9, 0xE0, 0x4C, 0x15, 0x89, 0x48, 0xFA, 0x68,
|
||||
0x90, 0x3F, 0xD9, 0xCF, 0x30, 0xA9, 0x57, 0x1B, 0x5F, 0x15, 0x15, 0x2E, 0xFC, 0x5B, 0x02, 0xD3,
|
||||
0xDD, 0xA2, 0xEF, 0x74, 0x79, 0xDF, 0x79, 0xD1, 0xDE, 0x4F, 0x29, 0xEB, 0x1E, 0xAC, 0xE1, 0x74,
|
||||
0x0F, 0xDA, 0x70, 0x14, 0xF9, 0x6D, 0x67, 0x06, 0xD3, 0x96, 0x37, 0x11, 0x09, 0x23, 0xDC, 0x3B,
|
||||
0x6F, 0x73, 0x03, 0x91, 0x7F, 0xA8, 0x3E, 0xDE, 0x25, 0x75, 0x52, 0x35, 0x8A, 0x99, 0x73, 0x32,
|
||||
0xCB, 0x9B, 0x27, 0x5F, 0x35, 0x6B, 0x4E, 0xD6, 0x6A, 0xBB, 0x4B, 0xD2, 0x70, 0x4B, 0x6C, 0x4C,
|
||||
0x3C, 0xFE, 0xD2, 0xE3, 0xA6, 0x80, 0xE4, 0x78, 0x25, 0x26, 0x4A, 0x5B, 0x1E, 0xED, 0x82, 0x4D,
|
||||
0x7D, 0x17, 0x6C, 0xF2, 0x1A, 0x15, 0xE1, 0x39, 0xFD, 0x46, 0x9D, 0xA6, 0x56, 0x3F, 0xFB, 0x96,
|
||||
0xF0, 0x74, 0x06, 0x9B, 0xD7, 0x34, 0xE0, 0x51, 0xAD, 0xAB, 0x77, 0xFB, 0xA9, 0x69, 0x7C, 0xB2,
|
||||
0x07, 0xD6, 0xB4, 0x9D, 0x2A, 0x98, 0x30, 0xEA, 0xD0, 0x4B, 0xB1, 0xD1, 0x16, 0x68, 0x8C, 0xB8,
|
||||
0xE2, 0xBF, 0xEF, 0x09, 0x8D, 0xD1, 0xC3, 0xD1, 0xF8, 0xCA, 0x1D, 0x66, 0xF4, 0x3D, 0xE0, 0x13,
|
||||
0xA0, 0xD1, 0x87, 0x6E, 0x1F, 0x6D, 0x8C, 0x51, 0xC2, 0xA7, 0x5A, 0xEF, 0xD1, 0x48, 0x79, 0xF1,
|
||||
0xFA, 0x72, 0x2F, 0x58, 0xA5, 0x93, 0x1E, 0x06, 0xAF, 0xCC, 0xE4, 0x43, 0x63, 0xE6, 0x61, 0x7F,
|
||||
0xF3, 0xA4, 0xE2, 0x4C, 0xAA, 0xF5, 0x0A, 0xFB, 0xA1, 0xD2, 0xA6, 0x41, 0xB2, 0xED, 0x68, 0x2F,
|
||||
0xA8, 0x89, 0x99, 0x0F, 0x03, 0x99, 0x34, 0xFA, 0xD0, 0x78, 0xF5, 0xFA, 0x24, 0x08, 0x68, 0xB0,
|
||||
0x31, 0x64, 0x09, 0x9F, 0x6A, 0xBD, 0x2C, 0xBF, 0x16, 0xA3, 0xBD, 0xC0, 0x95, 0xCE, 0x7A, 0x18,
|
||||
0xC4, 0x32, 0x9B, 0x0F, 0x0D, 0xDA, 0xD0, 0xF5, 0xC8, 0x60, 0x63, 0xC8, 0x04, 0x97, 0x6A, 0xFD,
|
||||
0x56, 0x7E, 0x0E, 0x9F, 0x7B, 0x81, 0x4B, 0xCE, 0x78, 0x18, 0xB0, 0x12, 0x6B, 0x0F, 0x0D, 0x95,
|
||||
0x63, 0x8F, 0x36, 0x06, 0x0A, 0x78, 0x54, 0xEB, 0xBA, 0xFD, 0xBB, 0xA2, 0x5D, 0xD3, 0x91, 0xCF,
|
||||
0x5F, 0xFC, 0x53, 0x6E, 0xDE, 0xE8, 0x7B, 0x41, 0x8C, 0x4F, 0x7D, 0x18, 0xBC, 0x84, 0xD1, 0x87,
|
||||
0x46, 0x4B, 0xBC, 0x04, 0xDC, 0x41, 0x9B, 0x97, 0xC3, 0x94, 0x91, 0xBF, 0xFB, 0x02, 0x23, 0xE5,
|
||||
0x0A, 0xED, 0xA7, 0x20, 0x66, 0xF3, 0xEE, 0x63, 0xD1, 0x3E, 0x33, 0xF2, 0xD0, 0x38, 0xB9, 0xC8,
|
||||
0xC6, 0x1F, 0x1C, 0xCC, 0xB6, 0x79, 0xF1, 0x22, 0xC7, 0xAB, 0x5A, 0xCF, 0xE1, 0x40, 0xB9, 0x16,
|
||||
0x07, 0xFB, 0x5A, 0x72, 0xE4, 0xE7, 0xDF, 0x07, 0x6A, 0x05, 0x7B, 0xBF, 0x0B, 0xE0, 0x60, 0x81,
|
||||
0x47, 0xBB, 0xFE, 0x56, 0xEF, 0x53, 0x17, 0xD8, 0x13, 0xF8, 0xDE, 0xCB, 0xE3, 0xFD, 0x02, 0x38,
|
||||
0x53, 0x62, 0x6F, 0x18, 0xE6, 0xEC, 0xDE, 0x07, 0x8C, 0xE9, 0x66, 0x04, 0xF1, 0x58, 0x40, 0xEE,
|
||||
0x41, 0x5E, 0x87, 0x94, 0x24, 0x93, 0x8F, 0x6E, 0x30, 0x2B, 0x87, 0x8C, 0x78, 0x9E, 0x6A, 0xBD,
|
||||
0xC0, 0x4C, 0xB9, 0xE5, 0xC3, 0xF3, 0x63, 0x49, 0xF0, 0x70, 0x29, 0xC9, 0x0B, 0xFF, 0x7C, 0xDF,
|
||||
0x38, 0xEA, 0xAB, 0xD6, 0x2D, 0xDF, 0x44, 0x0D, 0xB2, 0xF8, 0xD1, 0xE6, 0xC2, 0x84, 0x13, 0xB1,
|
||||
0x1F, 0x50, 0x50, 0x2A, 0x03, 0x29, 0xD9, 0xAA, 0xAA, 0x2A, 0xE9, 0x28, 0x77, 0xCE, 0xBA, 0x11,
|
||||
0xC4, 0x0A, 0x8F, 0xB2, 0xF5, 0xD3, 0xF1, 0x6F, 0x61, 0xED, 0xD5, 0x5F, 0xD6, 0x9E, 0x1F, 0xFB,
|
||||
0x68, 0x89, 0xBB, 0x57, 0xA0, 0x70, 0x2E, 0x77, 0xB1, 0xAF, 0x10, 0x95, 0x6D, 0xA6, 0x10, 0x9E,
|
||||
0x98, 0xED, 0xA7, 0xC9, 0xCC, 0x9A, 0xDB, 0x67, 0x93, 0x3E, 0xB0, 0x7D, 0x58, 0xD2, 0x8A, 0x1D,
|
||||
0x37, 0x49, 0x3F, 0xE4, 0xC3, 0xCC, 0xFD, 0xFF, 0xFB, 0xEF, 0xBA, 0x98, 0x21, 0xFD, 0x6E, 0x4E,
|
||||
0x31, 0x55, 0x09, 0x03, 0xBB, 0xA5, 0xAE, 0xDA, 0x9A, 0xB1, 0xC2, 0xF2, 0xE3, 0x65, 0xA6, 0xCF,
|
||||
0x11, 0x2F, 0xF1, 0xF5, 0x79, 0x68, 0x07, 0x64, 0xC0, 0xAC, 0x7F, 0x39, 0xD4, 0x8E, 0xFA, 0xD8,
|
||||
0x67, 0x15, 0xE4, 0x38, 0x37, 0x43, 0x18, 0xBC, 0x22, 0x21, 0xC3, 0xE0, 0x05, 0xAD, 0x74, 0xFD,
|
||||
0xF6, 0x75, 0x5B, 0x6E, 0x51, 0x79, 0x45, 0x91, 0x83, 0x9D, 0x92, 0xE1, 0x46, 0xBE, 0x90, 0xA3,
|
||||
0xE9, 0xD3, 0x74, 0xA8, 0x74, 0xB4, 0x2B, 0x7D, 0xEA, 0x41, 0xD0, 0xB6, 0x9B, 0xB2, 0x3C, 0x68,
|
||||
0x57, 0x15, 0x9E, 0xE3, 0xFA, 0xD4, 0x46, 0x21, 0x2E, 0xA5, 0x89, 0x5E, 0x32, 0xDB, 0xAD, 0xAB,
|
||||
0x4A, 0xB2, 0xF6, 0xB9, 0xA8, 0xF1, 0x0D, 0x4F, 0x60, 0xF4, 0x5D, 0x53, 0x10, 0x89, 0x47, 0x8A,
|
||||
0x25, 0x53, 0x8C, 0xE5, 0x97, 0xF3, 0x65, 0xEA, 0x63, 0xC9, 0x22, 0x1E, 0x5C, 0xE6, 0x89, 0x65,
|
||||
0x64, 0xA5, 0xD4, 0x51, 0xA7, 0x4F, 0x18, 0xA7, 0x2C, 0xD5, 0x4A, 0x09, 0x55, 0x52, 0x4A, 0xCC,
|
||||
0x00, 0xB3, 0x28, 0xF0, 0x9B, 0x31, 0x00, 0x1B, 0x32, 0xE5, 0xBA, 0xF5, 0xF1, 0x87, 0xA9, 0x1D,
|
||||
0x1F, 0x8B, 0x97, 0x5D, 0xA9, 0x77, 0x31, 0x44, 0x41, 0xEB, 0x87, 0xE9, 0x55, 0x85, 0x38, 0xF1,
|
||||
0x63, 0x98, 0x03, 0xC6, 0xED, 0xF8, 0x63, 0xD3, 0xE5, 0xFF, 0x71, 0x41, 0xBB, 0xD6, 0x2B, 0xAC,
|
||||
0x87, 0x7D, 0xED, 0xA6, 0x65, 0x4D, 0x39, 0x37, 0xF5, 0x70, 0xC5, 0xA3, 0x5D, 0xED, 0x63, 0x80,
|
||||
0x3F, 0x47, 0x18, 0x84, 0x31, 0xAA, 0xFC, 0x30, 0xBD, 0x8E, 0x15, 0x97, 0xF8, 0x24, 0xEC, 0x61,
|
||||
0xC7, 0x50, 0x42, 0x86, 0x58, 0x14, 0x9A, 0x70, 0xFA, 0xA6, 0x22, 0xC7, 0xF1, 0x47, 0x3D, 0xD6,
|
||||
0x63, 0x98, 0x46, 0xB1, 0x5B, 0x99, 0x97, 0x3D, 0x6A, 0x8B, 0x57, 0x3A, 0x2B, 0x34, 0x20, 0x5D,
|
||||
0xE2, 0x37, 0xA5, 0x6E, 0xB8, 0x75, 0x05, 0x33, 0x81, 0x7B, 0x78, 0x48, 0x71, 0x00, 0x38, 0x1A,
|
||||
0x5A, 0x49, 0xC6, 0x61, 0x49, 0x8F, 0x0D, 0x77, 0x81, 0x20, 0xC0, 0x7D, 0x3A, 0xC4, 0x79, 0x9A,
|
||||
0xEE, 0x72, 0x21, 0x69, 0x7E, 0x96, 0x74, 0xE3, 0x2A, 0xDB, 0x6B, 0xDE, 0x3A, 0xAA, 0xC6, 0x46,
|
||||
0x6F, 0xA5, 0xD0, 0x15, 0x3C, 0xB5, 0xD8, 0x20, 0x2D, 0xED, 0xCA, 0x68, 0x1B, 0xD7, 0x3A, 0x70,
|
||||
0x5E, 0xB7, 0x8E, 0x34, 0x3F, 0xF2, 0xBC, 0xA3, 0xD6, 0xB5, 0xFE, 0xF7, 0xDF, 0xD7, 0x4D, 0x1E,
|
||||
0x04, 0x37, 0xCD, 0x19, 0xE2, 0xAD, 0x56, 0x4B, 0x86, 0xC2, 0x05, 0x38, 0x32, 0xC3, 0xDE, 0x68,
|
||||
0xB7, 0x8E, 0x8E, 0xDA, 0x46, 0x76, 0xDC, 0x6A, 0xEB, 0xA6, 0xB8, 0x2E, 0x80, 0x36, 0x92, 0x4F,
|
||||
0x38, 0x6B, 0x5C, 0x3F, 0x7E, 0x7C, 0x73, 0xD4, 0x6A, 0xB5, 0x2F, 0x78, 0x88, 0x99, 0x47, 0x70,
|
||||
0xA8, 0x95, 0x10, 0xB6, 0xA5, 0x5C, 0xE2, 0x5C, 0xB4, 0x2F, 0xB0, 0x36, 0xD4, 0x4D, 0x97, 0xFF,
|
||||
0x29, 0xA1, 0x6E, 0xFE, 0x82, 0xE6, 0x6A, 0x4C, 0x37, 0xB0, 0x16, 0xEA, 0x20, 0x1C, 0xF3, 0xB1,
|
||||
0x2B, 0xC6, 0xA5, 0xF4, 0xAD, 0xA4, 0x1C, 0xAD, 0xAB, 0x8D, 0x75, 0x13, 0xF3, 0x3F, 0xA5, 0x62,
|
||||
0xE3, 0x48, 0x69, 0x60, 0xDE, 0xF6, 0x45, 0x4F, 0xF3, 0x75, 0xB3, 0x0B, 0x7F, 0x74, 0x3D, 0x6E,
|
||||
0x66, 0x70, 0x42, 0x34, 0x04, 0x93, 0x5B, 0x11, 0xB1, 0x34, 0xB8, 0xF4, 0x3C, 0xAD, 0x24, 0x77,
|
||||
0xE0, 0x95, 0xF4, 0x0A, 0x74, 0xA2, 0x1B, 0xC4, 0xB3, 0x41, 0xF8, 0x98, 0xFA, 0xB6, 0x47, 0xEC,
|
||||
0xBB, 0x96, 0xC6, 0x1D, 0x87, 0x21, 0x45, 0xE4, 0xDE, 0xE0, 0x37, 0xD4, 0xC1, 0x7A, 0x1C, 0x83,
|
||||
0x7A, 0x22, 0xEE, 0x64, 0x84, 0xCA, 0xF0, 0xF9, 0x98, 0xC4, 0x60, 0x96, 0x73, 0x90, 0x66, 0x32,
|
||||
0xA2, 0x95, 0xAB, 0xCA, 0xA7, 0x90, 0x27, 0x61, 0xBC, 0x84, 0xE4, 0x3E, 0xD5, 0x8A, 0x3D, 0x36,
|
||||
0xA7, 0x63, 0x1B, 0x94, 0x22, 0x1A, 0x80, 0xF2, 0x67, 0x1B, 0xEC, 0xFD, 0xCB, 0x38, 0xAA, 0xF1,
|
||||
0xD0, 0xD5, 0x93, 0xE8, 0xFC, 0x34, 0x0B, 0x5F, 0xE8, 0x53, 0x37, 0x1E, 0xE6, 0xC3, 0xAB, 0xC9,
|
||||
0xCF, 0x10, 0x5C, 0xB2, 0x72, 0x41, 0x98, 0xDC, 0xAD, 0xA3, 0x99, 0x95, 0x57, 0xA0, 0xF6, 0x56,
|
||||
0x53, 0x67, 0x9D, 0x10, 0xC8, 0xFA, 0xAB, 0xC9, 0x0A, 0xAD, 0x0E, 0x48, 0xFD, 0xD5, 0xA4, 0xB9,
|
||||
0x46, 0x06, 0x84, 0x74, 0x35, 0x61, 0xBE, 0x7C, 0x03, 0xE5, 0x40, 0x82, 0x35, 0x22, 0xBE, 0x43,
|
||||
0x47, 0x90, 0xD3, 0x74, 0xA0, 0x81, 0x4A, 0x15, 0xE2, 0x83, 0x0D, 0x2F, 0x7F, 0x7D, 0xFD, 0xAA,
|
||||
0x55, 0xCA, 0x37, 0xD8, 0x52, 0x6C, 0x7C, 0x96, 0x0C, 0x9F, 0x2A, 0xBC, 0x8E, 0x73, 0x28, 0x7F,
|
||||
0x2A, 0x99, 0x67, 0xB5, 0x12, 0x07, 0x94, 0x53, 0x7C, 0x84, 0x18, 0xBC, 0x5B, 0x90, 0x40, 0x07,
|
||||
0x99, 0x80, 0xA6, 0x57, 0x0C, 0x13, 0x3E, 0xDF, 0x4C, 0x18, 0x54, 0x2E, 0x34, 0x00, 0xF8, 0xF1,
|
||||
0xC5, 0x07, 0xBB, 0x03, 0xD5, 0xEA, 0x1A, 0x31, 0x5C, 0xF1, 0xE9, 0x08, 0xC2, 0x40, 0x4A, 0x8E,
|
||||
0x0D, 0xBA, 0xC8, 0x8F, 0xC5, 0x85, 0x7E, 0xF1, 0x82, 0x84, 0xF5, 0xAA, 0x38, 0x3D, 0x04, 0x7B,
|
||||
0x4E, 0xB5, 0xE6, 0xD5, 0x05, 0xB0, 0x9B, 0x9F, 0x41, 0xBA, 0xE1, 0x17, 0xB9, 0x3B, 0x90, 0x04,
|
||||
0xB1, 0xB1, 0x55, 0x9C, 0x65, 0xB9, 0xD0, 0xE3, 0x05, 0x5F, 0x88, 0xE3, 0xB9, 0x9D, 0x45, 0x5A,
|
||||
0xB0, 0x1A, 0x1C, 0x9E, 0xDF, 0xBA, 0x11, 0xDE, 0x4B, 0x90, 0xFB, 0x66, 0x15, 0x68, 0xD9, 0x3D,
|
||||
0x41, 0x36, 0xFF, 0xBD, 0x5F, 0x49, 0x6F, 0x06, 0x45, 0xBD, 0xC0, 0xCC, 0x40, 0x37, 0x82, 0xAC,
|
||||
0x63, 0xAD, 0xA8, 0x28, 0x71, 0xA2, 0x79, 0x74, 0x8F, 0x62, 0x98, 0x6B, 0x3E, 0xBC, 0x97, 0x20,
|
||||
0xFF, 0x4E, 0x05, 0xE8, 0x12, 0x2D, 0xE8, 0x12, 0xE9, 0x46, 0x94, 0xE9, 0x92, 0x95, 0xBD, 0x74,
|
||||
0xF6, 0xD1, 0x3D, 0xC2, 0xD3, 0x82, 0xA7, 0x1B, 0xE3, 0xD5, 0x54, 0x85, 0x57, 0x24, 0x41, 0x81,
|
||||
0xD1, 0x82, 0x02, 0x23, 0xDD, 0x18, 0x65, 0x0A, 0x64, 0x25, 0x33, 0x55, 0x60, 0xB2, 0x26, 0xFD,
|
||||
0xE4, 0x0D, 0x15, 0xE8, 0xF0, 0x65, 0x0D, 0xE1, 0xAC, 0xF8, 0xEA, 0xC6, 0xE5, 0x3D, 0xB4, 0xE9,
|
||||
0x1E, 0x4F, 0xD0, 0xF5, 0x72, 0x41, 0xD7, 0x4B, 0xDD, 0x78, 0x72, 0x7E, 0x29, 0x1B, 0x09, 0x14,
|
||||
0x6F, 0xA2, 0x4D, 0x78, 0x45, 0x33, 0x88, 0xF6, 0x85, 0x7F, 0x42, 0xF0, 0x4E, 0xE6, 0x58, 0x92,
|
||||
0xBA, 0x9A, 0x31, 0x5D, 0x68, 0xC8, 0xC3, 0x01, 0xD3, 0x4A, 0xEF, 0x3C, 0x0C, 0xAB, 0x8C, 0xE4,
|
||||
0xAD, 0x4B, 0xA5, 0xFD, 0xF3, 0x73, 0x85, 0x06, 0x8A, 0xF8, 0x0F, 0x03, 0x4A, 0x90, 0xED, 0x50,
|
||||
0x55, 0xE4, 0x26, 0x72, 0x05, 0xF3, 0x7F, 0xCB, 0x01, 0x21, 0xA5, 0xB0, 0x1E, 0x09, 0x15, 0x17,
|
||||
0xF3, 0xFD, 0x1B, 0xF8, 0x88, 0x63, 0x4F, 0x89, 0xA3, 0x24, 0x5A, 0xE8, 0x26, 0x3F, 0xD2, 0x3A,
|
||||
0xDA, 0x44, 0x37, 0x8E, 0x26, 0xA9, 0x47, 0x41, 0x4B, 0xDE, 0x5B, 0x32, 0x15, 0x41, 0xC7, 0x2F,
|
||||
0x07, 0xD1, 0xF1, 0x4B, 0x41, 0xC7, 0x2F, 0x00, 0xD8, 0x2C, 0x03, 0x7A, 0x52, 0x43, 0x30, 0xA3,
|
||||
0xAA, 0x27, 0xBD, 0x10, 0x5A, 0x57, 0x33, 0xBF, 0xCC, 0x4C, 0x16, 0x95, 0xF2, 0x48, 0x6E, 0xD7,
|
||||
0x3E, 0x3F, 0x16, 0xFF, 0x6A, 0xEE, 0xFF, 0x81, 0x09, 0x07, 0x8B, 0x81, 0x4E, 0x00, 0x00
|
||||
|
||||
//File: index_ov2640.html.gz, Size: 4316
|
||||
#define index_ov2640_html_gz_len 4316
|
||||
const uint8_t index_ov2640_html_gz[] = {
|
||||
0x1F, 0x8B, 0x08, 0x08, 0x50, 0x5C, 0xAE, 0x5C, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F,
|
||||
0x6F, 0x76, 0x32, 0x36, 0x34, 0x30, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0x00, 0xE5, 0x5D, 0x7B, 0x73,
|
||||
0xD3, 0xC6, 0x16, 0xFF, 0x9F, 0x4F, 0x21, 0x04, 0x25, 0xF6, 0x34, 0x76, 0x6C, 0xC7, 0x84, 0xE0,
|
||||
0xDA, 0xE2, 0x42, 0x08, 0xD0, 0x19, 0x5E, 0x25, 0x2D, 0x74, 0xA6, 0xD3, 0x81, 0xB5, 0xB4, 0xB2,
|
||||
0x55, 0x64, 0xC9, 0x95, 0x56, 0x76, 0x52, 0x26, 0x9F, 0xE3, 0x7E, 0xA0, 0xFB, 0xC5, 0xEE, 0xD9,
|
||||
0x87, 0xA4, 0x95, 0xBC, 0x7A, 0xD8, 0x26, 0x36, 0x97, 0xEB, 0xCC, 0x14, 0xD9, 0xDA, 0x73, 0xF6,
|
||||
0x9C, 0xF3, 0x3B, 0xAF, 0x5D, 0x3D, 0x3A, 0xBC, 0x6D, 0xF9, 0x26, 0xB9, 0x9A, 0x63, 0x6D, 0x4A,
|
||||
0x66, 0xAE, 0x71, 0x6B, 0xC8, 0xFF, 0xD1, 0xE0, 0x33, 0x9C, 0x62, 0x64, 0xF1, 0x43, 0xF6, 0x75,
|
||||
0x86, 0x09, 0xD2, 0xCC, 0x29, 0x0A, 0x42, 0x4C, 0x46, 0x7A, 0x44, 0xEC, 0xD6, 0xA9, 0x9E, 0x3F,
|
||||
0xED, 0xA1, 0x19, 0x1E, 0xE9, 0x0B, 0x07, 0x2F, 0xE7, 0x7E, 0x40, 0x74, 0xCD, 0xF4, 0x3D, 0x82,
|
||||
0x3D, 0x18, 0xBE, 0x74, 0x2C, 0x32, 0x1D, 0x59, 0x78, 0xE1, 0x98, 0xB8, 0xC5, 0xBE, 0x1C, 0x3A,
|
||||
0x9E, 0x43, 0x1C, 0xE4, 0xB6, 0x42, 0x13, 0xB9, 0x78, 0xD4, 0x95, 0x79, 0x11, 0x87, 0xB8, 0xD8,
|
||||
0x38, 0xBF, 0x78, 0x7B, 0xDC, 0xD3, 0xDE, 0xBC, 0xEF, 0xF5, 0x4F, 0x3A, 0xC3, 0x23, 0xFE, 0x5B,
|
||||
0x3A, 0x26, 0x24, 0x57, 0xF2, 0x77, 0xFA, 0x19, 0xFB, 0xD6, 0x95, 0xF6, 0x25, 0xF3, 0x13, 0xFD,
|
||||
0xD8, 0x20, 0x44, 0xCB, 0x46, 0x33, 0xC7, 0xBD, 0x1A, 0x68, 0x8F, 0x03, 0x98, 0xF3, 0xF0, 0x05,
|
||||
0x76, 0x17, 0x98, 0x38, 0x26, 0x3A, 0x0C, 0x91, 0x17, 0xB6, 0x42, 0x1C, 0x38, 0xF6, 0x4F, 0x2B,
|
||||
0x84, 0x63, 0x64, 0x7E, 0x9E, 0x04, 0x7E, 0xE4, 0x59, 0x03, 0xED, 0x4E, 0xF7, 0x94, 0xFE, 0xAD,
|
||||
0x0E, 0x32, 0x7D, 0xD7, 0x0F, 0xE0, 0xFC, 0xF9, 0x33, 0xFA, 0xB7, 0x7A, 0x9E, 0xCD, 0x1E, 0x3A,
|
||||
0xFF, 0xE0, 0x81, 0xD6, 0x3D, 0x99, 0x5F, 0x66, 0xCE, 0x5F, 0xDF, 0xCA, 0x7C, 0x9D, 0xF6, 0x8A,
|
||||
0xA4, 0x17, 0xF4, 0xA7, 0xE5, 0xF4, 0x21, 0x36, 0x89, 0xE3, 0x7B, 0xED, 0x19, 0x72, 0x3C, 0x05,
|
||||
0x27, 0xCB, 0x09, 0xE7, 0x2E, 0x02, 0x1B, 0xD8, 0x2E, 0x2E, 0xE5, 0x73, 0x67, 0x86, 0xBD, 0xE8,
|
||||
0xB0, 0x82, 0x1B, 0x65, 0xD2, 0xB2, 0x9C, 0x80, 0x8F, 0x1A, 0x50, 0x3B, 0x44, 0x33, 0xAF, 0x92,
|
||||
0x6D, 0x99, 0x5C, 0x9E, 0xEF, 0x61, 0x85, 0x01, 0xE9, 0x44, 0xCB, 0x00, 0xCD, 0xE9, 0x00, 0xFA,
|
||||
0xEF, 0xEA, 0x90, 0x99, 0xE3, 0x71, 0xA7, 0x1A, 0x68, 0xC7, 0xFD, 0xCE, 0xFC, 0xB2, 0x02, 0xCA,
|
||||
0xE3, 0x13, 0xFA, 0xB7, 0x3A, 0x68, 0x8E, 0x2C, 0xCB, 0xF1, 0x26, 0x03, 0xED, 0x54, 0xC9, 0xC2,
|
||||
0x0F, 0x2C, 0x1C, 0xB4, 0x02, 0x64, 0x39, 0x51, 0x38, 0xD0, 0xFA, 0xAA, 0x31, 0x33, 0x14, 0x4C,
|
||||
0x40, 0x16, 0xE2, 0x83, 0xB0, 0xAD, 0xAE, 0x52, 0x12, 0x31, 0x24, 0x70, 0x26, 0x53, 0x02, 0x90,
|
||||
0xAE, 0x8C, 0xC9, 0x1B, 0x4D, 0x84, 0x50, 0x15, 0x9E, 0xA5, 0x76, 0x53, 0x5B, 0x0D, 0xB9, 0xCE,
|
||||
0xC4, 0x6B, 0x39, 0x04, 0xCF, 0x40, 0x9D, 0x90, 0x04, 0x98, 0x98, 0xD3, 0x32, 0x51, 0x6C, 0x67,
|
||||
0x12, 0x05, 0x58, 0x21, 0x48, 0x62, 0xB7, 0x12, 0x85, 0xE1, 0xE4, 0xEA, 0xA9, 0xD6, 0x12, 0x8F,
|
||||
0x3F, 0x3B, 0xA4, 0x25, 0x6C, 0x32, 0xC6, 0xB6, 0x1F, 0x60, 0xE5, 0xC8, 0x78, 0x84, 0xEB, 0x9B,
|
||||
0x9F, 0x5B, 0x21, 0x41, 0x01, 0xA9, 0xC3, 0x10, 0xD9, 0x04, 0x07, 0xD5, 0xFC, 0x30, 0xF5, 0x8A,
|
||||
0x6A, 0x6E, 0xC5, 0xD3, 0x8A, 0x01, 0x8E, 0xE7, 0x3A, 0x1E, 0xAE, 0x2F, 0x5E, 0xD1, 0xBC, 0x59,
|
||||
0x76, 0x7C, 0x54, 0x0D, 0x60, 0x9C, 0xD9, 0xA4, 0xCC, 0x4B, 0x98, 0xAE, 0xAB, 0x93, 0x89, 0xB8,
|
||||
0xE9, 0x76, 0x3A, 0x3F, 0xAC, 0x9E, 0x9C, 0x62, 0xEE, 0xA6, 0x28, 0x22, 0xFE, 0xF6, 0x11, 0xB1,
|
||||
0x12, 0x56, 0x39, 0x3D, 0xFE, 0x35, 0xC3, 0x96, 0x83, 0xB4, 0x86, 0x14, 0xCE, 0xA7, 0x1D, 0xF0,
|
||||
0xA9, 0xA6, 0x86, 0x3C, 0x4B, 0x6B, 0xF8, 0x81, 0x03, 0x81, 0x80, 0x58, 0xBA, 0x71, 0xE1, 0x17,
|
||||
0x28, 0x1C, 0x73, 0xDC, 0x54, 0xA8, 0x5C, 0x12, 0x33, 0xB2, 0x45, 0xD4, 0x61, 0x43, 0x3F, 0x35,
|
||||
0x52, 0x0E, 0xFD, 0x54, 0x06, 0x90, 0x42, 0x47, 0xC6, 0xBE, 0x0C, 0x2F, 0x59, 0xC2, 0x22, 0xCC,
|
||||
0xE8, 0x67, 0x86, 0x2E, 0x5B, 0xA5, 0xD8, 0xC5, 0x83, 0x62, 0x0C, 0xA1, 0xCC, 0x9A, 0x0D, 0x18,
|
||||
0xBA, 0x98, 0x6A, 0x2D, 0x8D, 0x66, 0xC9, 0xA6, 0x9A, 0x46, 0x30, 0x55, 0x43, 0x4E, 0x3F, 0xB2,
|
||||
0x53, 0xAC, 0xA1, 0xAE, 0x5A, 0xD5, 0x34, 0x77, 0xF0, 0x3F, 0x95, 0x0F, 0x71, 0x4D, 0x0A, 0xB3,
|
||||
0x08, 0xFD, 0xD4, 0xCF, 0x24, 0x29, 0xB3, 0xCA, 0x6C, 0xA2, 0x60, 0x5C, 0x9C, 0x51, 0x56, 0xF8,
|
||||
0x16, 0x45, 0xB7, 0x82, 0x6B, 0xB9, 0x08, 0x75, 0xB3, 0x8B, 0x82, 0x71, 0x99, 0x0C, 0x95, 0x59,
|
||||
0x86, 0x7E, 0xAE, 0x6B, 0xF4, 0x1B, 0x77, 0xC6, 0x11, 0x21, 0xBE, 0x17, 0x6E, 0x55, 0xA2, 0x8A,
|
||||
0xE2, 0xEC, 0xAF, 0x28, 0x24, 0x8E, 0x7D, 0xD5, 0x12, 0x21, 0x0D, 0x71, 0x36, 0x47, 0xD0, 0x42,
|
||||
0x8E, 0x31, 0x59, 0x62, 0x5C, 0xDE, 0x6E, 0x78, 0x68, 0x01, 0x79, 0x67, 0x32, 0x71, 0x55, 0xBE,
|
||||
0x67, 0x46, 0x41, 0x48, 0xFB, 0xB6, 0xB9, 0xEF, 0x00, 0xE3, 0x60, 0x75, 0xE2, 0x6C, 0x0C, 0xD6,
|
||||
0x9C, 0xA8, 0x65, 0x8E, 0x15, 0x73, 0xF9, 0x11, 0xA1, 0x36, 0x56, 0x22, 0xE1, 0x83, 0x3A, 0x0E,
|
||||
0xB9, 0x52, 0x9E, 0x13, 0x91, 0xA8, 0x38, 0x13, 0x87, 0x60, 0x69, 0x59, 0xC8, 0xCA, 0x35, 0x30,
|
||||
0xA7, 0xD8, 0xFC, 0x8C, 0xAD, 0x1F, 0x2B, 0xDB, 0xB0, 0xAA, 0xF6, 0xB0, 0xED, 0x78, 0xF3, 0x88,
|
||||
0xB4, 0x68, 0x3B, 0x35, 0xBF, 0x11, 0xCC, 0x99, 0x43, 0xC6, 0x2A, 0xF6, 0x7A, 0x65, 0x4D, 0xC5,
|
||||
0xFD, 0xF9, 0x65, 0xB9, 0x11, 0x64, 0x61, 0x0D, 0x17, 0x8D, 0xB1, 0x5B, 0x26, 0xB2, 0x08, 0x86,
|
||||
0x82, 0xB4, 0x2B, 0x72, 0x55, 0x71, 0xEF, 0xC6, 0x24, 0x4B, 0x8B, 0x57, 0xFF, 0xC1, 0x0F, 0xB5,
|
||||
0xED, 0xC8, 0x8E, 0x0F, 0x33, 0x3F, 0x85, 0xD8, 0x85, 0x00, 0x2B, 0x6A, 0xBD, 0x61, 0xCC, 0x12,
|
||||
0x64, 0x28, 0x9D, 0x20, 0x40, 0xDE, 0x04, 0x43, 0x2E, 0xB8, 0x3C, 0x8C, 0x0F, 0xCB, 0x17, 0x06,
|
||||
0xB5, 0xD4, 0xA7, 0xA9, 0xFA, 0x7E, 0xF9, 0x42, 0x84, 0x27, 0x84, 0x0D, 0x9A, 0x11, 0x09, 0xD6,
|
||||
0xD2, 0xF9, 0xBB, 0x4A, 0xA7, 0xE0, 0xFD, 0x88, 0x32, 0x60, 0xB2, 0x2E, 0xA5, 0xEC, 0xEF, 0x2B,
|
||||
0x33, 0x42, 0xBC, 0xD2, 0xB3, 0xED, 0xAA, 0xB5, 0xA2, 0x6D, 0x1F, 0x77, 0x8E, 0xFB, 0x95, 0x0D,
|
||||
0x93, 0x52, 0xCB, 0xDC, 0x7A, 0x51, 0x91, 0x31, 0x92, 0x6C, 0x52, 0x0D, 0xC1, 0x60, 0xEA, 0x2F,
|
||||
0x70, 0xA0, 0x00, 0x22, 0x27, 0x6E, 0xFF, 0x61, 0xDF, 0xAA, 0xC1, 0x0D, 0x41, 0xBE, 0x5F, 0xA8,
|
||||
0xB2, 0x69, 0x96, 0x5D, 0xAF, 0x6B, 0xF6, 0x4A, 0x1D, 0x93, 0xB3, 0x6B, 0x83, 0x37, 0xA0, 0xB1,
|
||||
0x8B, 0xAD, 0x92, 0xF4, 0x6C, 0x61, 0x1B, 0x45, 0x2E, 0xA9, 0xB0, 0x37, 0xEA, 0xD0, 0xBF, 0xB2,
|
||||
0x19, 0x59, 0x5C, 0xFD, 0x41, 0x37, 0x3A, 0x46, 0x2C, 0x12, 0xFE, 0x54, 0xCC, 0x19, 0xD7, 0x4E,
|
||||
0x34, 0x9F, 0x63, 0x04, 0xA3, 0x4C, 0x5C, 0xB4, 0x24, 0xAD, 0xD5, 0x33, 0xAB, 0x13, 0x57, 0xAD,
|
||||
0x85, 0x68, 0xA5, 0x2B, 0x26, 0xDD, 0xD0, 0x5A, 0x3A, 0x0F, 0x6C, 0xDF, 0x8C, 0x54, 0x65, 0xBA,
|
||||
0x9E, 0x4B, 0xAD, 0xF2, 0x1B, 0xC4, 0x26, 0x0B, 0x5D, 0x87, 0x39, 0x76, 0xE4, 0x79, 0x14, 0xD1,
|
||||
0x16, 0x09, 0x40, 0x4D, 0xC5, 0x44, 0xF5, 0x0C, 0xB7, 0x51, 0x74, 0x66, 0x0C, 0x5B, 0xB4, 0x19,
|
||||
0x93, 0x0B, 0x40, 0x45, 0xA2, 0x48, 0x72, 0x88, 0x16, 0xFA, 0xA0, 0x54, 0xCC, 0x6A, 0x3B, 0xBB,
|
||||
0x90, 0x69, 0x34, 0x53, 0x35, 0x06, 0xF1, 0x64, 0x5D, 0xA8, 0x62, 0x7C, 0xBA, 0x60, 0x32, 0x46,
|
||||
0x8D, 0xCE, 0x61, 0xE7, 0xF0, 0x18, 0xFE, 0xA3, 0x68, 0xD0, 0xCB, 0x9D, 0x4B, 0x98, 0xB7, 0xC0,
|
||||
0xF3, 0x72, 0xC9, 0xA7, 0x7A, 0x9F, 0xA4, 0x28, 0x8D, 0x55, 0x62, 0x51, 0x3F, 0x92, 0xB2, 0x1B,
|
||||
0x26, 0xDD, 0x76, 0x45, 0x61, 0x29, 0x70, 0xE9, 0xF5, 0x1D, 0x51, 0xE1, 0x2D, 0xEB, 0x42, 0x3C,
|
||||
0xF3, 0xFF, 0x69, 0xF1, 0xAA, 0xFA, 0x7F, 0xEF, 0xED, 0x92, 0x29, 0xBE, 0x6B, 0x4F, 0x5F, 0xDB,
|
||||
0x2E, 0xE1, 0xBE, 0x7D, 0xA3, 0x53, 0x8C, 0x7A, 0x4B, 0xF4, 0x33, 0x20, 0xA1, 0x07, 0x8B, 0xAA,
|
||||
0x00, 0x56, 0x57, 0x85, 0x3D, 0x8F, 0x34, 0x66, 0x03, 0x1B, 0xD8, 0x8E, 0xEB, 0xB6, 0x5C, 0x7F,
|
||||
0x59, 0xDD, 0x89, 0x94, 0x7B, 0xF2, 0x8A, 0x9F, 0x56, 0xBB, 0xFC, 0xA6, 0xD2, 0x46, 0x90, 0xB9,
|
||||
0xFE, 0x27, 0xA4, 0xFD, 0xBE, 0x03, 0xAE, 0x34, 0x34, 0x36, 0x2B, 0x14, 0x1B, 0xF8, 0xE3, 0x76,
|
||||
0x13, 0xD5, 0x72, 0x25, 0xDE, 0x09, 0x96, 0x2E, 0xE6, 0xC2, 0xA5, 0x43, 0xCC, 0xE9, 0x06, 0x8B,
|
||||
0xAA, 0xB9, 0x1F, 0x3A, 0xFC, 0x1A, 0x4D, 0x80, 0x5D, 0x44, 0x3B, 0xF8, 0x8D, 0x96, 0xDC, 0x95,
|
||||
0x0B, 0x13, 0x99, 0xBC, 0x8E, 0x26, 0xCC, 0x74, 0xDF, 0xCE, 0x76, 0x49, 0x9B, 0xF7, 0x0E, 0xC5,
|
||||
0xB9, 0x5A, 0xED, 0xD6, 0x15, 0xED, 0x7E, 0x36, 0x32, 0xD4, 0x83, 0xD6, 0xC8, 0xE8, 0x71, 0xD2,
|
||||
0x9E, 0x04, 0xF8, 0xAA, 0x86, 0x32, 0x87, 0xE2, 0xDF, 0x01, 0xDF, 0x10, 0xDD, 0x7C, 0xED, 0xCF,
|
||||
0x0A, 0x80, 0xF0, 0xA2, 0x76, 0x3F, 0xAC, 0x31, 0x75, 0xF1, 0x94, 0x75, 0xFC, 0x31, 0xD9, 0xEE,
|
||||
0xD3, 0xF5, 0x1A, 0xE9, 0xA6, 0xA4, 0x84, 0xAA, 0x5D, 0x35, 0xAE, 0xBE, 0xCA, 0x93, 0x2E, 0xB6,
|
||||
0x49, 0xC1, 0xD5, 0x0C, 0xD6, 0xA7, 0x1E, 0x97, 0x67, 0xB7, 0x96, 0xB4, 0x4F, 0x50, 0x99, 0x39,
|
||||
0x92, 0x5D, 0xB9, 0x62, 0xEF, 0x53, 0x72, 0xA6, 0xD9, 0x73, 0x6D, 0xE6, 0xC5, 0x90, 0xC4, 0xED,
|
||||
0x33, 0x83, 0x19, 0xC6, 0xCC, 0x44, 0xC9, 0x07, 0x78, 0xF0, 0xEF, 0x8D, 0xDE, 0x89, 0xF2, 0x62,
|
||||
0x41, 0xC9, 0xE0, 0x32, 0xD1, 0x0A, 0xB7, 0xB5, 0x56, 0x4B, 0x56, 0xE1, 0x02, 0x59, 0xCE, 0x45,
|
||||
0x4A, 0xA0, 0xCA, 0xA3, 0xB2, 0x2C, 0xC3, 0xAC, 0xEE, 0xD1, 0x94, 0x3A, 0xBB, 0x33, 0x43, 0xD0,
|
||||
0xF6, 0x52, 0x77, 0x45, 0xC0, 0x51, 0x85, 0x5F, 0x1D, 0x77, 0x97, 0x36, 0x0D, 0xBB, 0x27, 0x9D,
|
||||
0x8A, 0x29, 0x4D, 0xD7, 0x0F, 0xCB, 0xE3, 0x0A, 0x8D, 0xC1, 0x7E, 0x11, 0x51, 0x4C, 0x24, 0xB6,
|
||||
0x2E, 0x95, 0x3B, 0x4F, 0xCC, 0xB9, 0x95, 0x67, 0x6A, 0x95, 0xEE, 0xD2, 0x98, 0x2A, 0x0F, 0xC7,
|
||||
0x9C, 0xCD, 0xBB, 0x1D, 0x65, 0xA6, 0x2D, 0xDD, 0x7F, 0x23, 0xF8, 0x12, 0xD6, 0x9B, 0xF4, 0x82,
|
||||
0xDC, 0x40, 0x33, 0xB1, 0x3A, 0x8D, 0x66, 0x8A, 0x5C, 0xB7, 0xCE, 0x26, 0x60, 0x29, 0x0E, 0x53,
|
||||
0xC7, 0xB2, 0x70, 0xE9, 0x2E, 0x27, 0x5D, 0xF3, 0xE6, 0x58, 0xC4, 0x47, 0xC3, 0x23, 0xE9, 0x06,
|
||||
0x96, 0xE1, 0x51, 0x7A, 0xAF, 0xCD, 0x90, 0xDE, 0xC5, 0x22, 0xDF, 0xE7, 0xC2, 0x2F, 0xB2, 0x68,
|
||||
0xA6, 0x8B, 0xC2, 0x70, 0xA4, 0xD3, 0xBB, 0x31, 0xF4, 0xEC, 0x6D, 0x2F, 0x43, 0xCB, 0x59, 0x68,
|
||||
0x8E, 0x35, 0xD2, 0x5D, 0x7F, 0xE2, 0xE7, 0xCE, 0xB1, 0xF3, 0x7C, 0xDB, 0x1B, 0x22, 0x75, 0xA4,
|
||||
0x67, 0x2E, 0x09, 0xE8, 0x8C, 0x2A, 0xFD, 0x49, 0x37, 0xEE, 0xDD, 0x79, 0xF8, 0xE0, 0xC1, 0xC9,
|
||||
0x4F, 0xF7, 0xBC, 0x71, 0x38, 0x17, 0xFF, 0xFD, 0x95, 0x5F, 0x41, 0x79, 0xF3, 0xBE, 0x77, 0xD2,
|
||||
0x87, 0x86, 0x16, 0x13, 0xE2, 0x78, 0x93, 0x70, 0x78, 0xC4, 0x98, 0xE6, 0x04, 0x39, 0x02, 0x49,
|
||||
0x0A, 0x64, 0x13, 0x09, 0x5D, 0x25, 0x5E, 0x3C, 0x24, 0x84, 0x1C, 0x35, 0x46, 0x81, 0x62, 0x08,
|
||||
0x1B, 0xC6, 0xDB, 0x05, 0xD6, 0x69, 0xE9, 0x2C, 0xB1, 0x8D, 0xFD, 0xCB, 0xBC, 0x06, 0x4C, 0x29,
|
||||
0x91, 0xF5, 0xC4, 0x28, 0x6C, 0x15, 0x31, 0x04, 0x32, 0x46, 0x4E, 0xAF, 0x87, 0x14, 0x8C, 0x49,
|
||||
0xE4, 0x13, 0xD6, 0x97, 0xB6, 0xE7, 0xF9, 0xD4, 0x76, 0x80, 0x66, 0x98, 0x26, 0x22, 0xF1, 0x63,
|
||||
0x31, 0x9B, 0x3C, 0x12, 0x09, 0xA5, 0x6E, 0xBC, 0xC3, 0x2C, 0x5C, 0x01, 0x65, 0xA5, 0x59, 0x57,
|
||||
0xB8, 0x88, 0x0C, 0x9A, 0x99, 0x5F, 0x8F, 0x45, 0x14, 0x3B, 0xA6, 0x2D, 0xC4, 0xDC, 0xA6, 0x42,
|
||||
0x20, 0xC6, 0xCE, 0x9F, 0x33, 0x07, 0x5B, 0x20, 0x37, 0x02, 0xD3, 0x76, 0x3B, 0xBA, 0xF1, 0xDB,
|
||||
0xEF, 0xCF, 0x1F, 0x37, 0x20, 0x11, 0x75, 0x2E, 0xBB, 0xBD, 0x4E, 0xA7, 0x39, 0x3C, 0xE2, 0x43,
|
||||
0xD6, 0xE6, 0xF5, 0x50, 0x37, 0x2E, 0x18, 0xAB, 0xDE, 0x29, 0xB0, 0xEA, 0xF4, 0xFA, 0x9B, 0xB3,
|
||||
0x3A, 0xD5, 0x0D, 0xC6, 0x09, 0x98, 0x5C, 0x3E, 0x38, 0x39, 0xDD, 0x9C, 0xD1, 0x03, 0x90, 0xE9,
|
||||
0x3D, 0x70, 0x3A, 0x05, 0xED, 0x4E, 0xB6, 0x51, 0xEE, 0x44, 0x37, 0x28, 0x1F, 0x88, 0x8A, 0xCB,
|
||||
0xFE, 0xE9, 0x16, 0x7C, 0xEE, 0xEB, 0xA2, 0x24, 0x52, 0x97, 0x8D, 0x8F, 0x74, 0xE3, 0xEC, 0xE7,
|
||||
0x67, 0x8D, 0x3E, 0xC8, 0xD8, 0x7B, 0x78, 0xB2, 0x39, 0xEF, 0xBE, 0x6E, 0xFC, 0x42, 0x85, 0x3C,
|
||||
0xEE, 0x01, 0xA3, 0xFE, 0x16, 0x42, 0x1E, 0xEB, 0xC6, 0x0B, 0xC6, 0x09, 0xB8, 0x5C, 0x76, 0x1F,
|
||||
0x6C, 0x21, 0x12, 0xB8, 0xD7, 0x2F, 0x8C, 0x13, 0xF8, 0x17, 0x75, 0xAF, 0x9A, 0x9C, 0x20, 0x5F,
|
||||
0x32, 0xD3, 0x94, 0xC4, 0xE9, 0x6A, 0xF6, 0xC9, 0x9C, 0x2E, 0x0B, 0xE3, 0xBF, 0x23, 0x28, 0x1D,
|
||||
0xE4, 0x6A, 0xED, 0x20, 0x16, 0x74, 0xA0, 0x12, 0x3F, 0xA8, 0x17, 0xBF, 0x92, 0x24, 0xC9, 0x65,
|
||||
0x39, 0xDD, 0xE8, 0x76, 0x2A, 0x34, 0x60, 0xB4, 0x72, 0x16, 0x64, 0xC4, 0x19, 0x05, 0x74, 0xDA,
|
||||
0x49, 0xB0, 0x18, 0xA6, 0xB7, 0x7E, 0x80, 0x8F, 0x1E, 0xEB, 0x52, 0x5C, 0x6F, 0x94, 0x22, 0x14,
|
||||
0xD2, 0xA2, 0x4B, 0xDD, 0x38, 0x39, 0xAE, 0xB2, 0xF7, 0x16, 0x70, 0x8C, 0x59, 0x9B, 0xE2, 0xE1,
|
||||
0x30, 0x5C, 0x1B, 0x91, 0x94, 0x54, 0x37, 0x9E, 0x24, 0xC7, 0xDB, 0xE0, 0xD2, 0xEA, 0x6D, 0x81,
|
||||
0x8B, 0x24, 0x0E, 0x87, 0xA6, 0xD5, 0x13, 0xD0, 0xF4, 0xF4, 0x34, 0x22, 0xBE, 0x26, 0x30, 0x55,
|
||||
0xD2, 0x6E, 0x83, 0x0B, 0x2D, 0xE2, 0x01, 0x0A, 0xC9, 0xDA, 0xA8, 0xC4, 0x84, 0x90, 0xD6, 0xC4,
|
||||
0xD1, 0xDE, 0x10, 0x49, 0x44, 0xF9, 0x0E, 0xF0, 0x08, 0x11, 0x89, 0x02, 0x76, 0x43, 0xDC, 0xDA,
|
||||
0x88, 0xA4, 0xA4, 0x50, 0x0F, 0x93, 0xE3, 0xBD, 0xA1, 0x22, 0x89, 0xF3, 0x3D, 0xE0, 0x32, 0xC7,
|
||||
0xA6, 0x83, 0xDC, 0x8F, 0xD8, 0xB6, 0xA1, 0x64, 0xAD, 0x8F, 0x4D, 0x86, 0x1C, 0xF0, 0xE1, 0xDF,
|
||||
0xB5, 0x73, 0xF6, 0x7D, 0xED, 0x1E, 0x31, 0xC7, 0xEE, 0x6B, 0x35, 0x8A, 0x1D, 0x75, 0xDF, 0xF2,
|
||||
0xDA, 0x4F, 0xE4, 0xDC, 0xB0, 0x43, 0xE8, 0x02, 0x13, 0x3C, 0x61, 0x2B, 0xE5, 0x8D, 0x79, 0xF4,
|
||||
0x74, 0xE3, 0x79, 0x80, 0xAE, 0xD8, 0xB3, 0x05, 0xDB, 0x34, 0x3D, 0xEF, 0xB0, 0xA5, 0xFD, 0x0A,
|
||||
0x4B, 0xC1, 0x6D, 0x3A, 0xB0, 0xE7, 0x01, 0x86, 0x65, 0xE2, 0x56, 0x5C, 0xEE, 0x43, 0x31, 0x83,
|
||||
0x83, 0xED, 0x98, 0x40, 0xC3, 0x7A, 0x81, 0xE7, 0x0E, 0xFA, 0x16, 0x1A, 0x2E, 0xB4, 0x1C, 0xAF,
|
||||
0x1D, 0x16, 0x40, 0xA3, 0x1B, 0x8F, 0x3F, 0x3C, 0x59, 0x3B, 0x49, 0xF1, 0xFD, 0xE6, 0x3A, 0x1E,
|
||||
0xCE, 0xB3, 0x93, 0x10, 0x50, 0x5F, 0x59, 0x6C, 0xAA, 0x23, 0xA7, 0xEE, 0x82, 0x53, 0xA1, 0x57,
|
||||
0x2C, 0x20, 0xDB, 0x9E, 0xD3, 0x25, 0x35, 0xEB, 0xE9, 0x78, 0x73, 0x19, 0x0C, 0x84, 0xF8, 0x38,
|
||||
0x41, 0xCE, 0xFA, 0x75, 0x25, 0x26, 0x64, 0x48, 0x69, 0xCF, 0xE1, 0x68, 0x57, 0x70, 0xF1, 0x69,
|
||||
0xF7, 0x86, 0x99, 0xD0, 0x7A, 0xDF, 0xC0, 0x81, 0x20, 0x33, 0xDF, 0x5A, 0x7F, 0x3B, 0x42, 0xD0,
|
||||
0xE9, 0x06, 0xA0, 0xF6, 0x0A, 0x0E, 0xD6, 0xAE, 0x32, 0x31, 0x83, 0x1B, 0x2E, 0x2F, 0x8F, 0x23,
|
||||
0xE2, 0x6F, 0x53, 0x59, 0x2E, 0x22, 0xCF, 0xBB, 0xDA, 0xA6, 0xAC, 0x9C, 0xB9, 0x7E, 0x64, 0x6D,
|
||||
0xCE, 0x01, 0x6A, 0xCA, 0x1B, 0xDB, 0x76, 0xCC, 0xCD, 0xAB, 0x12, 0x54, 0x94, 0x17, 0xFE, 0xAC,
|
||||
0x26, 0xFD, 0x0D, 0x67, 0x71, 0x6C, 0xAE, 0x9F, 0x20, 0xB0, 0x09, 0x28, 0x9E, 0x9F, 0x69, 0x17,
|
||||
0xE7, 0xAF, 0x2F, 0xDE, 0xBC, 0xDB, 0x4D, 0x76, 0x80, 0x39, 0xF7, 0x94, 0x18, 0xA8, 0xB6, 0xFB,
|
||||
0xCE, 0x09, 0x20, 0x44, 0x6F, 0x13, 0x9C, 0x7A, 0x1C, 0xA8, 0xA7, 0x17, 0x6F, 0x77, 0x85, 0x52,
|
||||
0x6F, 0x7F, 0x30, 0xF5, 0xBE, 0x05, 0x9C, 0x3E, 0xBA, 0x78, 0x81, 0xDD, 0x0D, 0xB0, 0xE2, 0x84,
|
||||
0x14, 0x2F, 0xED, 0x25, 0x3D, 0xDA, 0xDB, 0x42, 0x2E, 0x11, 0xE5, 0x3B, 0x58, 0xC6, 0x81, 0x57,
|
||||
0x7C, 0x64, 0x42, 0x6F, 0x12, 0x3C, 0x9C, 0x52, 0x37, 0xCE, 0x2F, 0xE7, 0x7E, 0x18, 0x05, 0x35,
|
||||
0x0B, 0xAA, 0x1A, 0x91, 0x6D, 0x76, 0x06, 0x53, 0x51, 0x38, 0x22, 0xF1, 0xD6, 0x20, 0xDD, 0xD9,
|
||||
0x4F, 0x30, 0xE9, 0x75, 0xFA, 0x5F, 0x15, 0x15, 0xCA, 0xFC, 0x26, 0x81, 0x99, 0x6C, 0x50, 0x77,
|
||||
0x26, 0xB4, 0xEE, 0x3C, 0x3F, 0xDB, 0x4D, 0x2A, 0x9B, 0xEC, 0xAD, 0xE0, 0x4C, 0xF6, 0x5A, 0x70,
|
||||
0x34, 0x7E, 0x51, 0x34, 0x81, 0x69, 0xC3, 0x45, 0x84, 0x20, 0x84, 0xB5, 0xF3, 0x26, 0x0B, 0x08,
|
||||
0x79, 0x53, 0xFD, 0x72, 0x9B, 0xD0, 0x89, 0xC5, 0xC8, 0x46, 0xCE, 0x71, 0x1A, 0x37, 0xF7, 0xBF,
|
||||
0x6A, 0xD4, 0x1C, 0x57, 0x4A, 0xBB, 0x4D, 0xD0, 0x50, 0x4D, 0x4C, 0xEC, 0xB8, 0xF4, 0x09, 0xA6,
|
||||
0x75, 0x01, 0x91, 0x68, 0x39, 0x26, 0xDA, 0x19, 0xFF, 0xB6, 0x0D, 0x36, 0xBD, 0x6D, 0xB0, 0x91,
|
||||
0x25, 0xCA, 0xC2, 0x73, 0x72, 0x43, 0x95, 0xA6, 0xDB, 0x3B, 0xBD, 0x49, 0x78, 0xC6, 0xF3, 0xF5,
|
||||
0x73, 0x1A, 0xD0, 0xE8, 0xC6, 0x93, 0xB7, 0xBB, 0xC9, 0x69, 0x74, 0xB2, 0x9A, 0x39, 0x6D, 0xAB,
|
||||
0x0C, 0xC6, 0x94, 0xDA, 0x77, 0x2B, 0xB6, 0xDC, 0x00, 0x8D, 0x25, 0x15, 0xFC, 0xC3, 0x8E, 0xD0,
|
||||
0x58, 0xD6, 0x47, 0xE3, 0x2B, 0x57, 0x98, 0xE5, 0xB7, 0x80, 0x4F, 0x80, 0x96, 0x1F, 0x27, 0x33,
|
||||
0xB4, 0x36, 0x46, 0x82, 0x4E, 0x37, 0xDE, 0xA1, 0xA5, 0xF6, 0xFC, 0xD5, 0xE3, 0x9D, 0x60, 0x15,
|
||||
0x4F, 0xBA, 0x1F, 0xBC, 0x12, 0x95, 0xF7, 0x8D, 0x99, 0x8B, 0xBD, 0xF5, 0x83, 0x8A, 0x12, 0xE9,
|
||||
0xC6, 0x4B, 0xEC, 0x85, 0xDA, 0x99, 0x1F, 0x88, 0xB7, 0xCD, 0xEC, 0x04, 0x35, 0x36, 0xF3, 0x7E,
|
||||
0x20, 0xE3, 0x4A, 0xEF, 0x1B, 0xAF, 0xE9, 0xCC, 0x09, 0x02, 0x3F, 0x58, 0x1B, 0x32, 0x41, 0xA7,
|
||||
0x1B, 0x2F, 0x5A, 0xAF, 0xD8, 0xD1, 0x4E, 0xE0, 0x8A, 0x67, 0xDD, 0x0F, 0x62, 0x89, 0xCE, 0xFB,
|
||||
0x06, 0x6D, 0x61, 0xBB, 0xCE, 0x7C, 0x6D, 0xC8, 0x18, 0x95, 0x6E, 0xBC, 0x6F, 0x3D, 0x83, 0x7F,
|
||||
0x77, 0x02, 0x17, 0x9F, 0x71, 0x3F, 0x60, 0x09, 0x6D, 0xF7, 0x0D, 0x95, 0x65, 0x2E, 0xD7, 0x06,
|
||||
0x0A, 0x68, 0x74, 0xE3, 0xE9, 0xD9, 0x07, 0xAD, 0xF1, 0xD4, 0x5F, 0x7A, 0xF4, 0xC6, 0x3F, 0xED,
|
||||
0xFC, 0x75, 0x73, 0x27, 0x88, 0xD1, 0xA9, 0xF7, 0x83, 0x17, 0x53, 0x7A, 0xDF, 0x68, 0xB1, 0xBB,
|
||||
0x8F, 0xC7, 0x68, 0xFD, 0x74, 0x18, 0x13, 0xD2, 0x7B, 0x5F, 0xE0, 0x48, 0x7B, 0x82, 0x76, 0x93,
|
||||
0x10, 0x93, 0x79, 0x77, 0xD1, 0xB4, 0xA7, 0x4A, 0xEE, 0x1B, 0x27, 0x1B, 0x99, 0xF8, 0xA3, 0x85,
|
||||
0xC9, 0x26, 0x37, 0x5E, 0x48, 0xB4, 0xBA, 0xF1, 0x0C, 0xBE, 0x68, 0x4F, 0xD9, 0x97, 0x5D, 0xB5,
|
||||
0x1C, 0xF2, 0xFC, 0xBB, 0x40, 0x2D, 0xA3, 0xEF, 0x37, 0x01, 0x1C, 0x34, 0x78, 0xFE, 0xC4, 0xDB,
|
||||
0xE8, 0x7E, 0xEA, 0x0C, 0xB9, 0x80, 0xEF, 0x1D, 0xFF, 0xBE, 0x5B, 0x00, 0x53, 0x21, 0x76, 0x86,
|
||||
0xA1, 0xA4, 0xF7, 0x2E, 0x60, 0x8C, 0x9F, 0x49, 0x60, 0xDB, 0x02, 0xFC, 0xE5, 0x4F, 0x55, 0x48,
|
||||
0x89, 0x57, 0xC2, 0xB0, 0xAD, 0x1B, 0x4C, 0x5A, 0x21, 0x71, 0x5C, 0x57, 0x37, 0x9E, 0x63, 0xA2,
|
||||
0x5D, 0xD0, 0xC3, 0xE1, 0x11, 0x1F, 0x50, 0x9F, 0x8B, 0xB8, 0xE1, 0x9F, 0xBE, 0x76, 0x0D, 0xCD,
|
||||
0x74, 0xE3, 0x82, 0xBE, 0x16, 0x0B, 0x78, 0xD1, 0x6F, 0xEB, 0x33, 0x63, 0x46, 0xC4, 0x5E, 0xE0,
|
||||
0x83, 0x50, 0x09, 0x48, 0xE2, 0xED, 0x24, 0xBA, 0x16, 0x1F, 0x49, 0xBF, 0x19, 0xE7, 0x6C, 0xB0,
|
||||
0x46, 0xBD, 0xAC, 0x7A, 0x3A, 0x7A, 0x15, 0xD6, 0x2C, 0xBE, 0x58, 0x3B, 0x3C, 0xF2, 0x90, 0xC2,
|
||||
0xDC, 0x05, 0x28, 0x0C, 0xF9, 0xFB, 0xD4, 0x0A, 0x58, 0x25, 0x0F, 0x53, 0x30, 0x4B, 0xA4, 0x0F,
|
||||
0x26, 0x25, 0x6A, 0xE5, 0x1F, 0x58, 0x12, 0x1B, 0xB6, 0xF5, 0x82, 0x96, 0x3D, 0x7A, 0x24, 0xEA,
|
||||
0x21, 0x3D, 0x4C, 0xCC, 0xFF, 0x9F, 0x7F, 0x57, 0xF9, 0x0C, 0x7D, 0xDB, 0x5D, 0x2A, 0x98, 0xAE,
|
||||
0x85, 0x81, 0x39, 0xD2, 0x8B, 0x1E, 0xCD, 0x28, 0xD0, 0xFC, 0x48, 0xA5, 0x7A, 0x6E, 0xB0, 0xC2,
|
||||
0xD6, 0xC3, 0xD0, 0x0C, 0x9C, 0x39, 0x31, 0x6E, 0x59, 0xBE, 0x19, 0xCD, 0xB0, 0x47, 0xDA, 0xC8,
|
||||
0xB2, 0xCE, 0x17, 0x70, 0xF0, 0xD2, 0x09, 0x09, 0x06, 0x2B, 0x34, 0x0E, 0x9E, 0xBE, 0x79, 0x75,
|
||||
0xC6, 0x1F, 0x51, 0x79, 0xE9, 0x23, 0x0B, 0x5B, 0x07, 0x87, 0x9A, 0x1D, 0x79, 0xDC, 0xCD, 0x1B,
|
||||
0x98, 0x8E, 0xE5, 0x6F, 0x1A, 0x5C, 0xA0, 0x40, 0x1B, 0xA3, 0x10, 0xBF, 0xF0, 0x43, 0xA2, 0x8D,
|
||||
0xB4, 0x84, 0xA3, 0xEB, 0x9B, 0xEC, 0xF6, 0xC5, 0xB6, 0x1F, 0x38, 0x13, 0xC7, 0x13, 0x23, 0xB9,
|
||||
0xB2, 0xBF, 0x05, 0x2E, 0x0C, 0x4D, 0xA8, 0x7E, 0xD4, 0x0E, 0x06, 0xA7, 0xDD, 0x03, 0xFA, 0x34,
|
||||
0x11, 0xC0, 0x00, 0x3F, 0x00, 0x04, 0x18, 0x06, 0x40, 0x80, 0x8F, 0x0C, 0xF1, 0x38, 0x11, 0x76,
|
||||
0xDB, 0xCC, 0xE4, 0x54, 0x40, 0x2A, 0x6D, 0xE3, 0x80, 0xE3, 0x74, 0x40, 0x1F, 0xAD, 0xBB, 0x4E,
|
||||
0x28, 0xC3, 0xA9, 0xBF, 0x2C, 0xA3, 0x0C, 0xF0, 0xCC, 0x5F, 0xE0, 0x1C, 0x71, 0x42, 0x2D, 0xBC,
|
||||
0xB9, 0x72, 0xEA, 0xD8, 0xEB, 0x0F, 0x9A, 0xF1, 0x80, 0xE4, 0xCD, 0x3D, 0x23, 0x8D, 0x04, 0x11,
|
||||
0xCE, 0xB2, 0xC5, 0x5E, 0x15, 0xD7, 0x58, 0xAC, 0x52, 0xC6, 0x36, 0x72, 0xC3, 0x1C, 0xE7, 0x68,
|
||||
0x6E, 0x21, 0x82, 0xDF, 0xD3, 0xDD, 0x5D, 0x18, 0xD0, 0xC0, 0xEE, 0x21, 0xDF, 0xEA, 0x3D, 0x14,
|
||||
0x67, 0xDE, 0x01, 0x5F, 0x82, 0x9B, 0xE9, 0xAC, 0xF2, 0xCF, 0x40, 0x91, 0xFD, 0x3A, 0xD2, 0xBC,
|
||||
0x08, 0x42, 0xF8, 0x11, 0x53, 0x41, 0x1B, 0x64, 0xCE, 0x32, 0x6A, 0x17, 0xB2, 0x93, 0x78, 0x4B,
|
||||
0x31, 0x9B, 0x93, 0xFD, 0xE8, 0xD8, 0x74, 0xE2, 0x36, 0x7B, 0x67, 0xF2, 0x08, 0x78, 0x1C, 0xC4,
|
||||
0xD9, 0xFD, 0x20, 0x7D, 0x15, 0xA5, 0x4C, 0xC4, 0xEC, 0xD0, 0x16, 0x7D, 0xB0, 0x38, 0xBF, 0x10,
|
||||
0x27, 0x6E, 0xDF, 0x5E, 0x24, 0x7C, 0x35, 0x69, 0x18, 0x9C, 0x4A, 0x4F, 0x5C, 0xC3, 0x09, 0xE9,
|
||||
0x79, 0xBF, 0x55, 0xDE, 0x39, 0x1E, 0x31, 0x73, 0x89, 0xC3, 0xAD, 0x44, 0xF2, 0x8C, 0x05, 0xEE,
|
||||
0xDD, 0xCB, 0x72, 0xBB, 0x3D, 0x12, 0x54, 0xA9, 0x26, 0x7C, 0x3C, 0x44, 0x06, 0x44, 0x1E, 0xA8,
|
||||
0x2D, 0x9E, 0x02, 0x15, 0x22, 0x39, 0x76, 0xE3, 0x76, 0xC6, 0xF0, 0x89, 0x8C, 0x36, 0x35, 0x91,
|
||||
0x63, 0x31, 0x03, 0xB1, 0x7B, 0x20, 0x9A, 0xE9, 0x53, 0x72, 0x5C, 0xBE, 0x47, 0xCC, 0xEB, 0x1B,
|
||||
0x58, 0x5C, 0x1D, 0x6D, 0x82, 0xFD, 0xA9, 0x33, 0xA7, 0x3F, 0x88, 0xF1, 0xE9, 0x54, 0x32, 0xC7,
|
||||
0x49, 0x86, 0x23, 0x55, 0x2C, 0x27, 0x37, 0xFD, 0x30, 0x7E, 0xF4, 0x3A, 0x81, 0xB8, 0x56, 0x21,
|
||||
0x3F, 0x95, 0xCA, 0x26, 0x07, 0x36, 0xF4, 0x5A, 0x46, 0xFA, 0x7B, 0xCE, 0xD4, 0xC9, 0xC0, 0x02,
|
||||
0x26, 0x6C, 0x82, 0x55, 0x26, 0xA5, 0x92, 0xC7, 0x37, 0x8A, 0x29, 0x0C, 0xC2, 0xD8, 0x2D, 0xC7,
|
||||
0xD4, 0x14, 0x6C, 0x56, 0x38, 0x2C, 0x63, 0x95, 0x2B, 0xFC, 0x0A, 0x86, 0x3C, 0x10, 0x1B, 0xBC,
|
||||
0xAE, 0x3D, 0x61, 0x35, 0x8A, 0x32, 0x17, 0x31, 0x96, 0xFD, 0xFD, 0x96, 0x2C, 0xFC, 0x75, 0x1C,
|
||||
0x76, 0x49, 0x0A, 0x94, 0xFD, 0x80, 0xFA, 0x7F, 0x6C, 0x69, 0x1A, 0x22, 0xA9, 0xA3, 0x89, 0x07,
|
||||
0xFB, 0xE3, 0xF8, 0x48, 0xE1, 0x30, 0x21, 0xF7, 0x49, 0x91, 0x32, 0xC8, 0x89, 0x2A, 0x87, 0x08,
|
||||
0xC8, 0xDD, 0xD5, 0xE4, 0x47, 0xF5, 0xC7, 0x90, 0x42, 0x3F, 0x67, 0xF8, 0xB0, 0x8B, 0x32, 0x09,
|
||||
0x13, 0xFE, 0x1B, 0xBF, 0xCD, 0xA9, 0xE5, 0x7B, 0x58, 0xCD, 0x5D, 0x0E, 0x12, 0x15, 0x4F, 0x5E,
|
||||
0xC2, 0xF3, 0x4C, 0xA3, 0xF1, 0xCC, 0x21, 0x0A, 0x86, 0x07, 0x90, 0xBE, 0x55, 0xBC, 0x44, 0x63,
|
||||
0x97, 0x12, 0x04, 0x98, 0x44, 0x81, 0x27, 0x47, 0x21, 0xCF, 0x64, 0x7F, 0x47, 0x38, 0xB8, 0x02,
|
||||
0x46, 0x9F, 0xEE, 0x7E, 0x89, 0xEB, 0xC2, 0xF5, 0x11, 0x7B, 0x34, 0xC1, 0x77, 0x1F, 0x41, 0xE5,
|
||||
0x18, 0xDD, 0xFD, 0xC2, 0xA0, 0xBE, 0xBE, 0x07, 0x53, 0xC2, 0x17, 0x36, 0xF1, 0xF5, 0x27, 0xCE,
|
||||
0xC2, 0xA6, 0x2F, 0x9A, 0x6D, 0x30, 0x16, 0x31, 0x6E, 0x6D, 0x32, 0xC5, 0x5E, 0x23, 0xC0, 0xE1,
|
||||
0x1C, 0xD8, 0xE3, 0x34, 0x01, 0xC6, 0x33, 0xFA, 0x2E, 0x86, 0x12, 0x35, 0x69, 0x7C, 0x0A, 0x30,
|
||||
0xD0, 0x81, 0x00, 0xC4, 0xD7, 0xEE, 0x7E, 0x61, 0x2C, 0xAE, 0x35, 0x1B, 0xB2, 0x40, 0x38, 0xC5,
|
||||
0xD6, 0x21, 0xD4, 0x2B, 0x44, 0xE8, 0x13, 0xB8, 0x77, 0xBF, 0xC4, 0xAC, 0xDA, 0xFC, 0xA7, 0xEB,
|
||||
0x4F, 0x89, 0x87, 0x24, 0x45, 0x24, 0xAE, 0x7D, 0xEC, 0x44, 0x9B, 0xF1, 0xBA, 0x60, 0x28, 0xF8,
|
||||
0xC1, 0x63, 0xD7, 0x6D, 0x1C, 0xF0, 0x07, 0x95, 0x45, 0x6E, 0x6F, 0x43, 0xB3, 0x7A, 0x8E, 0x40,
|
||||
0x6C, 0xB9, 0x28, 0xB0, 0x7C, 0xE5, 0x7B, 0xA6, 0xEB, 0x98, 0x9F, 0x69, 0x42, 0x6F, 0x66, 0x05,
|
||||
0xE7, 0x19, 0xC2, 0x6D, 0xF3, 0x17, 0xCF, 0xBC, 0xF6, 0x2D, 0x9C, 0x73, 0xD3, 0x26, 0x15, 0xE3,
|
||||
0xE8, 0x08, 0xAC, 0x8C, 0xAC, 0x38, 0x95, 0x71, 0x8C, 0xE8, 0x1B, 0x0A, 0xB8, 0x99, 0x32, 0x16,
|
||||
0xE6, 0xCA, 0x08, 0x5D, 0xB8, 0xCD, 0xD2, 0x2A, 0x1F, 0xAB, 0x9C, 0xBA, 0x2D, 0x47, 0x4F, 0x4B,
|
||||
0x6C, 0xF1, 0x57, 0xE8, 0x7B, 0x8D, 0xE6, 0xAD, 0xC4, 0x0C, 0xAB, 0x3C, 0xE8, 0x04, 0x12, 0x83,
|
||||
0x8C, 0x89, 0x8A, 0xCC, 0x94, 0x5D, 0x0D, 0x1C, 0xA4, 0x99, 0xA4, 0xC0, 0x66, 0xF4, 0x23, 0x55,
|
||||
0x42, 0x56, 0x06, 0xD9, 0xBC, 0x7F, 0x30, 0x97, 0xF9, 0xF3, 0x90, 0x97, 0x4E, 0x29, 0x23, 0x35,
|
||||
0x25, 0x73, 0x71, 0xFF, 0xA3, 0xAF, 0xE8, 0x97, 0xDB, 0x17, 0xE8, 0xC9, 0xCF, 0x5D, 0x4C, 0x0F,
|
||||
0x9F, 0x5C, 0xFD, 0x0C, 0x25, 0x9F, 0x37, 0x2E, 0x4C, 0x96, 0x94, 0xE0, 0x2C, 0x69, 0x1A, 0x2B,
|
||||
0x29, 0xD3, 0x06, 0x53, 0xE2, 0xC1, 0x9A, 0x7E, 0x9E, 0x6F, 0xCA, 0x38, 0x24, 0xEB, 0x83, 0x0C,
|
||||
0x29, 0xE5, 0x5A, 0x4D, 0x9B, 0x59, 0x15, 0x48, 0xF4, 0x72, 0xAE, 0x2B, 0xA3, 0x97, 0x16, 0x02,
|
||||
0x12, 0x35, 0x73, 0xE4, 0x6A, 0x62, 0xB9, 0x25, 0x3E, 0x90, 0x8C, 0x1D, 0x12, 0x7F, 0xCE, 0x57,
|
||||
0x26, 0x39, 0x27, 0x5F, 0x3A, 0x9E, 0xE5, 0x2F, 0xDB, 0xF4, 0x7C, 0x43, 0x94, 0x56, 0x59, 0xD1,
|
||||
0xB6, 0xE3, 0x81, 0x01, 0x5F, 0xFC, 0xFA, 0xEA, 0x25, 0x4D, 0x39, 0xF2, 0x0A, 0xE7, 0x20, 0xDB,
|
||||
0x17, 0xB1, 0x77, 0x02, 0x2B, 0x67, 0xA0, 0xB0, 0xB5, 0xA1, 0xD5, 0xE6, 0xA9, 0x26, 0x69, 0x47,
|
||||
0x69, 0x24, 0xD0, 0xC3, 0x4F, 0x7C, 0x4E, 0x5A, 0x78, 0x32, 0x00, 0x37, 0x2B, 0x65, 0xF1, 0xE7,
|
||||
0x79, 0x51, 0x20, 0x0E, 0x1F, 0x13, 0x02, 0xEE, 0xAA, 0x71, 0x47, 0x0E, 0x69, 0x8E, 0x11, 0xAB,
|
||||
0xC3, 0x5B, 0x9A, 0x0C, 0x7E, 0x41, 0xC8, 0xA7, 0x66, 0x12, 0x31, 0x96, 0x15, 0x5E, 0xCA, 0x93,
|
||||
0x68, 0x0E, 0x71, 0x89, 0x1F, 0x7D, 0x34, 0xC7, 0x90, 0x1A, 0x9F, 0x82, 0xE7, 0xB7, 0x3D, 0xD0,
|
||||
0xA0, 0x79, 0x5D, 0xA6, 0x0E, 0x37, 0x57, 0x0A, 0x64, 0x5D, 0x21, 0x58, 0x12, 0x52, 0x73, 0xCB,
|
||||
0xD8, 0x47, 0xCD, 0x4E, 0xF6, 0xDE, 0x73, 0x2F, 0x6E, 0x6D, 0x8B, 0x0C, 0x3B, 0x5A, 0x35, 0x2D,
|
||||
0xEF, 0x6E, 0x32, 0x0C, 0xD2, 0xF4, 0xB2, 0x22, 0x6C, 0xAE, 0x81, 0x91, 0xFC, 0x22, 0x1E, 0x10,
|
||||
0xCB, 0x2E, 0x07, 0x44, 0x81, 0xEC, 0xD9, 0xDE, 0x2F, 0xD7, 0x2C, 0xE4, 0x20, 0x17, 0x39, 0x4C,
|
||||
0xA3, 0x2F, 0x2A, 0x98, 0xD2, 0xF2, 0x2C, 0x9C, 0xA0, 0x4E, 0x99, 0x50, 0xE6, 0xBF, 0xD2, 0x7A,
|
||||
0xC1, 0x67, 0x88, 0xA5, 0xCD, 0xF7, 0xA8, 0xD9, 0xDA, 0x70, 0x16, 0x81, 0x95, 0x66, 0xB1, 0x4F,
|
||||
0xF2, 0xDF, 0x68, 0xC3, 0x96, 0x04, 0x0F, 0x34, 0x70, 0x65, 0x41, 0x0D, 0xA7, 0xA5, 0x4C, 0x20,
|
||||
0xBA, 0xBD, 0x0A, 0x02, 0xE9, 0xAE, 0x27, 0x89, 0x56, 0xEA, 0x22, 0x4B, 0xD3, 0x5F, 0xFE, 0x3E,
|
||||
0x1D, 0xC6, 0x02, 0xB8, 0xAE, 0x6A, 0xAE, 0xC0, 0x09, 0xC6, 0x35, 0x13, 0xB7, 0xA1, 0x44, 0xA2,
|
||||
0xAD, 0x92, 0x9C, 0xA6, 0xA0, 0x2D, 0x5E, 0x6D, 0x89, 0x73, 0xDE, 0x54, 0xD4, 0x0A, 0xAF, 0xB6,
|
||||
0xC1, 0xD7, 0x92, 0x83, 0xC4, 0xF7, 0x3F, 0xA6, 0x26, 0xC4, 0xE5, 0xF6, 0xC6, 0xB2, 0xBD, 0xE3,
|
||||
0xE5, 0x40, 0x05, 0x85, 0x7C, 0x9B, 0x26, 0x37, 0x17, 0xAE, 0x69, 0x2E, 0x2C, 0xCC, 0x45, 0x09,
|
||||
0xD2, 0x0E, 0xB4, 0x7A, 0x6D, 0x92, 0xF8, 0xFF, 0x87, 0x27, 0xA9, 0x66, 0xCB, 0x71, 0xA9, 0x9C,
|
||||
0xA2, 0xF7, 0x97, 0xD4, 0x2B, 0x27, 0xC8, 0x3C, 0xCB, 0xC1, 0xD5, 0x5A, 0x8E, 0xEB, 0xA9, 0x15,
|
||||
0xAF, 0x1D, 0x28, 0x41, 0xAA, 0x96, 0x7A, 0x85, 0x11, 0xAB, 0x92, 0xEC, 0x75, 0xB3, 0xFF, 0xDD,
|
||||
0x42, 0xF2, 0x66, 0x89, 0x44, 0x58, 0xBE, 0x51, 0x5C, 0x59, 0x3D, 0xF9, 0x30, 0x49, 0xC9, 0x64,
|
||||
0x8D, 0x52, 0x49, 0x9A, 0x8C, 0x94, 0xA8, 0x13, 0x39, 0x4A, 0xA9, 0xE3, 0x41, 0xBC, 0xEC, 0x26,
|
||||
0x5F, 0x6B, 0x19, 0x2B, 0x19, 0x9D, 0x06, 0x4E, 0xCA, 0x80, 0x77, 0xFC, 0x86, 0x76, 0x3F, 0xBF,
|
||||
0x26, 0xE6, 0xBD, 0x17, 0x57, 0x36, 0xD7, 0x71, 0xC9, 0x03, 0x12, 0x95, 0x32, 0x63, 0x92, 0x00,
|
||||
0xE1, 0xF4, 0x45, 0x62, 0x56, 0x8A, 0x82, 0x5C, 0x1C, 0x90, 0x86, 0xFE, 0xD6, 0xC5, 0x74, 0xBD,
|
||||
0x22, 0x9E, 0xC6, 0x39, 0xFB, 0xF9, 0x99, 0xE6, 0x07, 0x1A, 0x7F, 0xC1, 0x5D, 0x90, 0xBC, 0x5B,
|
||||
0x44, 0x13, 0x6F, 0x7F, 0x62, 0xAB, 0x42, 0x9A, 0x83, 0xC8, 0xD4, 0x09, 0xA1, 0x49, 0xA6, 0x4F,
|
||||
0xDE, 0xE2, 0xDB, 0x7A, 0xF2, 0x82, 0xA7, 0x4A, 0xF5, 0x78, 0x57, 0xFC, 0x53, 0xA2, 0x48, 0xCE,
|
||||
0x9C, 0x9C, 0x26, 0xB5, 0xE5, 0x6D, 0xA1, 0xE3, 0x4A, 0x22, 0x2A, 0x5B, 0x87, 0xAE, 0x61, 0xC2,
|
||||
0xE4, 0xF4, 0x37, 0x6B, 0x45, 0xB5, 0x02, 0x95, 0x86, 0x4C, 0xC8, 0x52, 0x5B, 0xA6, 0xBA, 0xAE,
|
||||
0x58, 0x53, 0xB5, 0xD8, 0x2F, 0x41, 0x94, 0xEE, 0x79, 0x29, 0xB3, 0x7C, 0x31, 0x2A, 0xDC, 0xE2,
|
||||
0xBC, 0xB0, 0xF2, 0xCF, 0xF0, 0x28, 0xDE, 0x59, 0xE5, 0xDF, 0xF8, 0xAB, 0x8B, 0x86, 0x47, 0xFC,
|
||||
0x7F, 0x22, 0xF6, 0x5F, 0x04, 0x9C, 0x39, 0x76, 0x5C, 0x6C, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
//File: index_ov3660.html.gz, Size: 4408
|
||||
#define index_ov3660_html_gz_len 4408
|
||||
const uint8_t index_ov3660_html_gz[] = {
|
||||
0x1F, 0x8B, 0x08, 0x08, 0x28, 0x5C, 0xAE, 0x5C, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F,
|
||||
0x6F, 0x76, 0x33, 0x36, 0x36, 0x30, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0x00, 0xE5, 0x5D, 0xEB, 0x92,
|
||||
0xD3, 0xC6, 0x12, 0xFE, 0xCF, 0x53, 0x08, 0x41, 0x58, 0x6F, 0x65, 0xED, 0xF5, 0x6D, 0xCD, 0xE2,
|
||||
0xD8, 0xE6, 0xC0, 0xB2, 0x84, 0x54, 0x01, 0x49, 0x20, 0x21, 0xA9, 0x4A, 0xA5, 0x60, 0x2C, 0x8D,
|
||||
0xED, 0x09, 0xB2, 0xE4, 0x48, 0x23, 0x7B, 0x37, 0xD4, 0x3E, 0xC7, 0x79, 0xA0, 0xF3, 0x62, 0xA7,
|
||||
0xE7, 0x22, 0x69, 0x24, 0x8F, 0x2E, 0xB6, 0x59, 0x9B, 0xC3, 0x31, 0x55, 0x20, 0x5B, 0xD3, 0x3D,
|
||||
0xDD, 0xFD, 0xF5, 0x6D, 0x46, 0x17, 0x06, 0x77, 0x6D, 0xCF, 0xA2, 0xD7, 0x0B, 0x6C, 0xCC, 0xE8,
|
||||
0xDC, 0x19, 0xDD, 0x19, 0x88, 0x7F, 0x0C, 0xF8, 0x0C, 0x66, 0x18, 0xD9, 0xE2, 0x90, 0x7F, 0x9D,
|
||||
0x63, 0x8A, 0x0C, 0x6B, 0x86, 0xFC, 0x00, 0xD3, 0xA1, 0x19, 0xD2, 0x49, 0xFD, 0xDC, 0xCC, 0x9E,
|
||||
0x76, 0xD1, 0x1C, 0x0F, 0xCD, 0x25, 0xC1, 0xAB, 0x85, 0xE7, 0x53, 0xD3, 0xB0, 0x3C, 0x97, 0x62,
|
||||
0x17, 0x86, 0xAF, 0x88, 0x4D, 0x67, 0x43, 0x1B, 0x2F, 0x89, 0x85, 0xEB, 0xFC, 0xCB, 0x09, 0x71,
|
||||
0x09, 0x25, 0xC8, 0xA9, 0x07, 0x16, 0x72, 0xF0, 0xB0, 0xA5, 0xF2, 0xA2, 0x84, 0x3A, 0x78, 0x74,
|
||||
0xF9, 0xF6, 0xA7, 0x4E, 0xDB, 0xF8, 0xF1, 0x5D, 0xA7, 0xD7, 0x6B, 0x0E, 0x4E, 0xC5, 0x6F, 0xC9,
|
||||
0x98, 0x80, 0x5E, 0xAB, 0xDF, 0xD9, 0x67, 0xEC, 0xD9, 0xD7, 0xC6, 0xA7, 0xD4, 0x4F, 0xEC, 0x33,
|
||||
0x01, 0x21, 0xEA, 0x13, 0x34, 0x27, 0xCE, 0x75, 0xDF, 0x78, 0xE2, 0xC3, 0x9C, 0x27, 0x2F, 0xB0,
|
||||
0xB3, 0xC4, 0x94, 0x58, 0xE8, 0x24, 0x40, 0x6E, 0x50, 0x0F, 0xB0, 0x4F, 0x26, 0xDF, 0xAD, 0x11,
|
||||
0x8E, 0x91, 0xF5, 0x71, 0xEA, 0x7B, 0xA1, 0x6B, 0xF7, 0x8D, 0x7B, 0xAD, 0x73, 0xF6, 0x67, 0x7D,
|
||||
0x90, 0xE5, 0x39, 0x9E, 0x0F, 0xE7, 0x2F, 0x9F, 0xB3, 0x3F, 0xEB, 0xE7, 0xF9, 0xEC, 0x01, 0xF9,
|
||||
0x07, 0xF7, 0x8D, 0x56, 0x6F, 0x71, 0x95, 0x3A, 0x7F, 0x73, 0x27, 0xF5, 0x75, 0xD6, 0xCE, 0x93,
|
||||
0x5E, 0xD2, 0x9F, 0x17, 0xD3, 0x07, 0xD8, 0xA2, 0xC4, 0x73, 0x1B, 0x73, 0x44, 0x5C, 0x0D, 0x27,
|
||||
0x9B, 0x04, 0x0B, 0x07, 0x81, 0x0D, 0x26, 0x0E, 0x2E, 0xE4, 0x73, 0x6F, 0x8E, 0xDD, 0xF0, 0xA4,
|
||||
0x84, 0x1B, 0x63, 0x52, 0xB7, 0x89, 0x2F, 0x46, 0xF5, 0x99, 0x1D, 0xC2, 0xB9, 0x5B, 0xCA, 0xB6,
|
||||
0x48, 0x2E, 0xD7, 0x73, 0xB1, 0xC6, 0x80, 0x6C, 0xA2, 0x95, 0x8F, 0x16, 0x6C, 0x00, 0xFB, 0x77,
|
||||
0x7D, 0xC8, 0x9C, 0xB8, 0xC2, 0xA9, 0xFA, 0x46, 0xA7, 0xDB, 0x5C, 0x5C, 0x95, 0x40, 0xD9, 0xE9,
|
||||
0xB1, 0x3F, 0xEB, 0x83, 0x16, 0xC8, 0xB6, 0x89, 0x3B, 0xED, 0x1B, 0xE7, 0x5A, 0x16, 0x9E, 0x6F,
|
||||
0x63, 0xBF, 0xEE, 0x23, 0x9B, 0x84, 0x41, 0xDF, 0xE8, 0xEA, 0xC6, 0xCC, 0x91, 0x3F, 0x05, 0x59,
|
||||
0xA8, 0x07, 0xC2, 0xD6, 0x5B, 0x5A, 0x49, 0xE4, 0x10, 0x9F, 0x4C, 0x67, 0x14, 0x20, 0x5D, 0x1B,
|
||||
0x93, 0x35, 0x9A, 0x0C, 0xA1, 0x32, 0x3C, 0x0B, 0xED, 0xA6, 0xB7, 0x1A, 0x72, 0xC8, 0xD4, 0xAD,
|
||||
0x13, 0x8A, 0xE7, 0xA0, 0x4E, 0x40, 0x7D, 0x4C, 0xAD, 0x59, 0x91, 0x28, 0x13, 0x32, 0x0D, 0x7D,
|
||||
0xAC, 0x11, 0x24, 0xB6, 0x5B, 0x81, 0xC2, 0x70, 0x72, 0xFD, 0x54, 0x7D, 0x85, 0xC7, 0x1F, 0x09,
|
||||
0xAD, 0x4B, 0x9B, 0x8C, 0xF1, 0xC4, 0xF3, 0xB1, 0x76, 0x64, 0x34, 0xC2, 0xF1, 0xAC, 0x8F, 0xF5,
|
||||
0x80, 0x22, 0x9F, 0x56, 0x61, 0x88, 0x26, 0x14, 0xFB, 0xE5, 0xFC, 0x30, 0xF3, 0x8A, 0x72, 0x6E,
|
||||
0xF9, 0xD3, 0xCA, 0x01, 0xC4, 0x75, 0x88, 0x8B, 0xAB, 0x8B, 0x97, 0x37, 0x6F, 0x9A, 0x9D, 0x18,
|
||||
0x55, 0x01, 0x18, 0x32, 0x9F, 0x16, 0x79, 0x09, 0xD7, 0x75, 0x7D, 0x32, 0x19, 0x37, 0xAD, 0x66,
|
||||
0xF3, 0x9B, 0xF5, 0x93, 0x33, 0x2C, 0xDC, 0x14, 0x85, 0xD4, 0xDB, 0x3D, 0x22, 0xD6, 0xC2, 0x2A,
|
||||
0xA3, 0xC7, 0xBF, 0xE6, 0xD8, 0x26, 0xC8, 0xA8, 0x29, 0xE1, 0x7C, 0xDE, 0x04, 0x9F, 0x3A, 0x36,
|
||||
0x90, 0x6B, 0x1B, 0x35, 0xCF, 0x27, 0x10, 0x08, 0x88, 0xA7, 0x1B, 0x07, 0x7E, 0x81, 0xC2, 0xB1,
|
||||
0xC0, 0xC7, 0x1A, 0x95, 0x0B, 0x62, 0x46, 0xB5, 0x88, 0x3E, 0x6C, 0xD8, 0xA7, 0x42, 0xCA, 0x61,
|
||||
0x9F, 0xD2, 0x00, 0xD2, 0xE8, 0xC8, 0xD9, 0x17, 0xE1, 0xA5, 0x4A, 0x98, 0x87, 0x19, 0xFB, 0xCC,
|
||||
0xD1, 0x55, 0xBD, 0x10, 0xBB, 0x68, 0x50, 0x84, 0x21, 0x94, 0x59, 0xAB, 0x06, 0x43, 0x97, 0x33,
|
||||
0xA3, 0x6E, 0xB0, 0x2C, 0x79, 0xAC, 0xA7, 0x91, 0x4C, 0xF5, 0x90, 0xB3, 0x8F, 0xEA, 0x14, 0x1B,
|
||||
0xA8, 0xAB, 0x57, 0x35, 0xC9, 0x1D, 0xE2, 0x8F, 0xCE, 0x87, 0x84, 0x26, 0xB9, 0x59, 0x84, 0x7D,
|
||||
0xAA, 0x67, 0x92, 0x84, 0x59, 0x69, 0x36, 0xD1, 0x30, 0xCE, 0xCF, 0x28, 0x6B, 0x7C, 0xF3, 0xA2,
|
||||
0x5B, 0xC3, 0xB5, 0x58, 0x84, 0xAA, 0xD9, 0x45, 0xC3, 0xB8, 0x48, 0x86, 0xD2, 0x2C, 0xC3, 0x3E,
|
||||
0x37, 0x15, 0xFA, 0x8D, 0x7B, 0xE3, 0x90, 0x52, 0xCF, 0x0D, 0x76, 0x2A, 0x51, 0x79, 0x71, 0xF6,
|
||||
0x57, 0x18, 0x50, 0x32, 0xB9, 0xAE, 0xCB, 0x90, 0x86, 0x38, 0x5B, 0x20, 0x68, 0x21, 0xC7, 0x98,
|
||||
0xAE, 0x30, 0x2E, 0x6E, 0x37, 0x5C, 0xB4, 0x84, 0xBC, 0x33, 0x9D, 0x3A, 0x3A, 0xDF, 0xB3, 0x42,
|
||||
0x3F, 0x60, 0x7D, 0xDB, 0xC2, 0x23, 0xC0, 0xD8, 0x5F, 0x9F, 0x38, 0x1D, 0x83, 0x15, 0x27, 0xAA,
|
||||
0x5B, 0x63, 0xCD, 0x5C, 0x5E, 0x48, 0x99, 0x8D, 0xB5, 0x48, 0x78, 0xA0, 0x0E, 0xA1, 0xD7, 0xDA,
|
||||
0x73, 0x32, 0x12, 0x35, 0x67, 0xA2, 0x10, 0x2C, 0x2C, 0x0B, 0x69, 0xB9, 0xFA, 0xD6, 0x0C, 0x5B,
|
||||
0x1F, 0xB1, 0xFD, 0x6D, 0x69, 0x1B, 0x56, 0xD6, 0x1E, 0x36, 0x88, 0xBB, 0x08, 0x69, 0x9D, 0xB5,
|
||||
0x53, 0x8B, 0x5B, 0xC1, 0x9C, 0x3B, 0x64, 0xA4, 0x62, 0xBB, 0x5D, 0xD4, 0x54, 0x9C, 0x2D, 0xAE,
|
||||
0x8A, 0x8D, 0xA0, 0x0A, 0x3B, 0x72, 0xD0, 0x18, 0x3B, 0x45, 0x22, 0xCB, 0x60, 0xC8, 0x49, 0xBB,
|
||||
0x32, 0x57, 0xE5, 0xF7, 0x6E, 0x5C, 0xB2, 0xA4, 0x78, 0x75, 0x1F, 0x7E, 0x53, 0xD9, 0x8E, 0xFC,
|
||||
0xF8, 0x24, 0xF5, 0x53, 0x80, 0x1D, 0x08, 0xB0, 0xBC, 0xD6, 0x1B, 0xC6, 0xAC, 0x40, 0x86, 0xC2,
|
||||
0x09, 0x7C, 0xE4, 0x4E, 0x31, 0xE4, 0x82, 0xAB, 0x93, 0xE8, 0xB0, 0x78, 0x61, 0x50, 0x49, 0x7D,
|
||||
0x96, 0xAA, 0xCF, 0x8A, 0x17, 0x22, 0x22, 0x21, 0x6C, 0xD1, 0x8C, 0x28, 0xB0, 0x16, 0xCE, 0xDF,
|
||||
0xD2, 0x3A, 0x85, 0xE8, 0x47, 0xB4, 0x01, 0x93, 0x76, 0x29, 0x6D, 0x7F, 0x5F, 0x9A, 0x11, 0xA2,
|
||||
0x95, 0xDE, 0x64, 0x52, 0xB6, 0x56, 0x9C, 0x4C, 0x3A, 0xCD, 0x4E, 0xB7, 0xB4, 0x61, 0xD2, 0x6A,
|
||||
0x99, 0x59, 0x2F, 0x6A, 0x32, 0x46, 0x9C, 0x4D, 0xCA, 0x21, 0xE8, 0xCF, 0xBC, 0x25, 0xF6, 0x35,
|
||||
0x40, 0x64, 0xC4, 0xED, 0x3E, 0xEA, 0xDA, 0x15, 0xB8, 0x21, 0xC8, 0xF7, 0x4B, 0x5D, 0x36, 0x4D,
|
||||
0xB3, 0x6B, 0xB7, 0xAC, 0x76, 0xA1, 0x63, 0x0A, 0x76, 0x0D, 0xF0, 0x06, 0x34, 0x76, 0xB0, 0x5D,
|
||||
0x90, 0x9E, 0x6D, 0x3C, 0x41, 0xA1, 0x43, 0x4B, 0xEC, 0x8D, 0x9A, 0xEC, 0x4F, 0xD1, 0x8C, 0x3C,
|
||||
0xAE, 0xFE, 0x60, 0x1B, 0x1D, 0x43, 0x1E, 0x09, 0x7F, 0x6A, 0xE6, 0x8C, 0x6A, 0x27, 0x5A, 0x2C,
|
||||
0x30, 0x82, 0x51, 0x16, 0xCE, 0x5B, 0x92, 0x56, 0xEA, 0x99, 0xF5, 0x89, 0xAB, 0xD2, 0x42, 0xB4,
|
||||
0xD4, 0x15, 0xE3, 0x6E, 0x68, 0x23, 0x9D, 0xFB, 0x13, 0xCF, 0x0A, 0x75, 0x65, 0xBA, 0x9A, 0x4B,
|
||||
0xAD, 0xF3, 0xEB, 0x47, 0x26, 0x0B, 0x1C, 0xC2, 0x1D, 0x3B, 0x74, 0x5D, 0x86, 0x68, 0x9D, 0xFA,
|
||||
0xA0, 0xA6, 0x66, 0xA2, 0x6A, 0x86, 0xDB, 0x2A, 0x3A, 0x53, 0x86, 0xCD, 0xDB, 0x8C, 0xC9, 0x04,
|
||||
0xA0, 0x26, 0x51, 0xC4, 0x39, 0xC4, 0x08, 0x3C, 0x50, 0x2A, 0x62, 0xB5, 0x9B, 0x5D, 0xE8, 0x2C,
|
||||
0x9C, 0xEB, 0x1A, 0x83, 0x68, 0xB2, 0x16, 0x54, 0x31, 0x31, 0x9D, 0x3F, 0x1D, 0xA3, 0x5A, 0xF3,
|
||||
0xA4, 0x79, 0xD2, 0x81, 0xBF, 0x34, 0x0D, 0x7A, 0xB1, 0x73, 0x49, 0xF3, 0xE6, 0x78, 0x5E, 0x26,
|
||||
0xF9, 0x94, 0xEF, 0x93, 0xE4, 0xA5, 0xB1, 0x52, 0x2C, 0xAA, 0x47, 0x52, 0x7A, 0xC3, 0xA4, 0xD5,
|
||||
0x28, 0x29, 0x2C, 0x39, 0x2E, 0xBD, 0xB9, 0x23, 0x6A, 0xBC, 0x65, 0x53, 0x88, 0xE7, 0xDE, 0x3F,
|
||||
0x75, 0x51, 0x55, 0xFF, 0xEF, 0xBD, 0x5D, 0x31, 0xC5, 0x57, 0xED, 0xE9, 0x1B, 0xDB, 0x25, 0x38,
|
||||
0xB4, 0x6F, 0x34, 0xF3, 0x51, 0xAF, 0xCB, 0x7E, 0x06, 0x24, 0x74, 0x61, 0x51, 0xE5, 0xC3, 0xEA,
|
||||
0x2A, 0xB7, 0xE7, 0x51, 0xC6, 0x6C, 0x61, 0x83, 0x09, 0x71, 0x9C, 0xBA, 0xE3, 0xAD, 0xCA, 0x3B,
|
||||
0x91, 0x62, 0x4F, 0x5E, 0xF3, 0xD3, 0x72, 0x97, 0xDF, 0x56, 0xDA, 0x10, 0x32, 0xD7, 0xFF, 0x84,
|
||||
0xB4, 0x5F, 0x77, 0xC0, 0x15, 0x86, 0xC6, 0x76, 0x85, 0x62, 0x0B, 0x7F, 0xDC, 0x6D, 0xA2, 0x4A,
|
||||
0xAE, 0x24, 0x3A, 0xC1, 0xC2, 0xC5, 0x5C, 0xB0, 0x22, 0xD4, 0x9A, 0x6D, 0xB1, 0xA8, 0x5A, 0x78,
|
||||
0x01, 0x11, 0xD7, 0x68, 0x7C, 0xEC, 0x20, 0xD6, 0xC1, 0x6F, 0xB5, 0xE4, 0x2E, 0x5D, 0x98, 0xA8,
|
||||
0xE4, 0x55, 0x34, 0xE1, 0xA6, 0xFB, 0x72, 0xB6, 0x4B, 0x1A, 0xA2, 0x77, 0xC8, 0xCF, 0xD5, 0x7A,
|
||||
0xB7, 0x2E, 0x69, 0xF7, 0xD3, 0x91, 0xA1, 0x1F, 0xB4, 0x41, 0x46, 0x8F, 0x92, 0xF6, 0xD4, 0xC7,
|
||||
0xD7, 0x15, 0x94, 0x39, 0x91, 0xFF, 0xF6, 0xC5, 0x86, 0xE8, 0xF6, 0x6B, 0x7F, 0x5E, 0x00, 0xA4,
|
||||
0x17, 0x35, 0xBA, 0x41, 0x85, 0xA9, 0xF3, 0xA7, 0xAC, 0xE2, 0x8F, 0xF1, 0x76, 0x9F, 0x69, 0x56,
|
||||
0x48, 0x37, 0x05, 0x25, 0x54, 0xEF, 0xAA, 0x51, 0xF5, 0xD5, 0x9E, 0x74, 0xF0, 0x84, 0xE6, 0x5C,
|
||||
0xCD, 0xE0, 0x7D, 0x6A, 0xA7, 0x38, 0xBB, 0xD5, 0x95, 0x7D, 0x82, 0xD2, 0xCC, 0x11, 0xEF, 0xCA,
|
||||
0xE5, 0x7B, 0x9F, 0x96, 0x33, 0xCB, 0x9E, 0x1B, 0x33, 0xCF, 0x87, 0x24, 0x6A, 0x9F, 0x39, 0xCC,
|
||||
0x30, 0x66, 0x2E, 0x4B, 0x3E, 0xC0, 0x83, 0x7F, 0xAF, 0xB5, 0x7B, 0xDA, 0x8B, 0x05, 0x05, 0x83,
|
||||
0x8B, 0x44, 0xCB, 0xDD, 0xD6, 0x5A, 0x2F, 0x59, 0xB9, 0x0B, 0x64, 0x35, 0x17, 0x69, 0x81, 0x2A,
|
||||
0x8E, 0xCA, 0xA2, 0x0C, 0xB3, 0xBE, 0x47, 0x53, 0xE8, 0xEC, 0x64, 0x8E, 0xA0, 0xED, 0x65, 0xEE,
|
||||
0x8A, 0x80, 0xA3, 0x0E, 0xBF, 0x2A, 0xEE, 0xAE, 0x6C, 0x1A, 0xB6, 0x7A, 0xCD, 0x92, 0x29, 0x2D,
|
||||
0xC7, 0x0B, 0x8A, 0xE3, 0x0A, 0x8D, 0xC1, 0x7E, 0x21, 0xD5, 0x4C, 0x24, 0xB7, 0x2E, 0xB5, 0x3B,
|
||||
0x4F, 0xDC, 0xB9, 0xB5, 0x67, 0x2A, 0x95, 0xEE, 0xC2, 0x98, 0x2A, 0x0E, 0xC7, 0x8C, 0xCD, 0x5B,
|
||||
0x4D, 0x6D, 0xA6, 0x2D, 0xDC, 0x7F, 0xA3, 0xF8, 0x0A, 0xD6, 0x9B, 0xEC, 0x82, 0x5C, 0xDF, 0xB0,
|
||||
0xB0, 0x3E, 0x8D, 0xA6, 0x8A, 0x5C, 0xAB, 0xCA, 0x26, 0x60, 0x21, 0x0E, 0x33, 0x62, 0xDB, 0xB8,
|
||||
0x70, 0x97, 0x93, 0xAD, 0x79, 0x2B, 0x36, 0x0F, 0x4C, 0x7E, 0xDD, 0xA6, 0xD4, 0xAD, 0x04, 0x45,
|
||||
0xE1, 0x75, 0xFA, 0xD6, 0x6D, 0x47, 0x8C, 0x2C, 0x34, 0x79, 0x7B, 0xC4, 0xE9, 0x56, 0xA4, 0x50,
|
||||
0x54, 0x6D, 0x70, 0xC7, 0xDB, 0xC4, 0xCC, 0x64, 0x60, 0x07, 0x36, 0x6A, 0x3D, 0x9B, 0x2B, 0x52,
|
||||
0x0D, 0x4E, 0x95, 0x7B, 0x89, 0x06, 0xA7, 0xC9, 0x6D, 0x4F, 0x03, 0x76, 0x43, 0x91, 0x7A, 0xCB,
|
||||
0x91, 0xB8, 0xDE, 0x65, 0x58, 0x0E, 0x0A, 0x82, 0xA1, 0xC9, 0x6E, 0x8C, 0x31, 0xD3, 0x77, 0x20,
|
||||
0x0D, 0x6C, 0xB2, 0x34, 0x88, 0x3D, 0x34, 0x1D, 0x6F, 0xEA, 0x65, 0xCE, 0xF1, 0xF3, 0xE2, 0x0A,
|
||||
0x04, 0x24, 0xCD, 0xA1, 0x99, 0xBA, 0x3A, 0x63, 0x72, 0xAA, 0xE4, 0x27, 0x73, 0xF4, 0xE0, 0xDE,
|
||||
0xA3, 0x87, 0x0F, 0x7B, 0xDF, 0x3D, 0x70, 0xC7, 0xC1, 0x42, 0xFE, 0xFD, 0x8B, 0xB8, 0x98, 0x25,
|
||||
0xEE, 0x88, 0x82, 0x3C, 0x4A, 0x29, 0xE8, 0x19, 0x0C, 0x4E, 0x39, 0xD3, 0x8C, 0x20, 0xA7, 0x20,
|
||||
0x49, 0x8E, 0x6C, 0xB2, 0xB6, 0xEA, 0xC4, 0x8B, 0x86, 0x04, 0x50, 0x2E, 0xC6, 0xC8, 0xD7, 0x0C,
|
||||
0xE1, 0xC3, 0x44, 0xE7, 0xC6, 0xFD, 0xD6, 0xE4, 0x35, 0x66, 0xEC, 0x5D, 0x65, 0x35, 0xE0, 0x4A,
|
||||
0xC9, 0x02, 0x24, 0x47, 0x61, 0x3B, 0x8F, 0x21, 0x90, 0x71, 0x72, 0x76, 0x69, 0x2A, 0x67, 0x4C,
|
||||
0x2C, 0x9F, 0xB4, 0xBE, 0x72, 0xA5, 0x44, 0x4C, 0x3D, 0xF1, 0xD1, 0x1C, 0x33, 0xF7, 0x97, 0x3F,
|
||||
0xE6, 0xB3, 0xC9, 0x22, 0x11, 0x53, 0x9A, 0xA3, 0x37, 0x98, 0x67, 0x4E, 0x40, 0x59, 0x6B, 0xD6,
|
||||
0x35, 0x2E, 0xB2, 0x98, 0xA5, 0xE6, 0x37, 0x23, 0x11, 0xE5, 0xE6, 0x75, 0x1D, 0x71, 0xB7, 0x29,
|
||||
0x11, 0x88, 0xB3, 0xF3, 0x16, 0xDC, 0xC1, 0x96, 0xC8, 0x09, 0xC1, 0xB4, 0xAD, 0x96, 0x39, 0xFA,
|
||||
0xF9, 0xF7, 0xEF, 0x9F, 0xD4, 0xDA, 0xCD, 0xEE, 0xF9, 0x55, 0xEB, 0xAC, 0xD7, 0x3D, 0x1E, 0x9C,
|
||||
0x8A, 0x21, 0x9B, 0xF3, 0x6A, 0x9A, 0xA3, 0x5F, 0x19, 0x2F, 0xA8, 0x2F, 0xCD, 0xAB, 0x56, 0xBB,
|
||||
0xD9, 0xDC, 0x9E, 0xD7, 0x23, 0x73, 0xF4, 0x96, 0xB3, 0x6A, 0x9F, 0x03, 0xAB, 0x66, 0x7B, 0x07,
|
||||
0xB1, 0xCE, 0xCD, 0x11, 0xE7, 0x04, 0x4C, 0xAE, 0x1E, 0xF6, 0xCE, 0xB7, 0x67, 0xF4, 0x10, 0x64,
|
||||
0x7A, 0x07, 0x9C, 0xCE, 0x41, 0xBB, 0xDE, 0x2E, 0xCA, 0xF5, 0xCC, 0x11, 0xE3, 0xD3, 0xEB, 0x36,
|
||||
0xAF, 0xBA, 0xE7, 0x3B, 0xF0, 0x39, 0x33, 0x65, 0xA7, 0xC3, 0xDC, 0x3F, 0x3A, 0x32, 0x47, 0x17,
|
||||
0x3F, 0x3C, 0xAF, 0x75, 0x41, 0xC6, 0xF6, 0xA3, 0xDE, 0xF6, 0xBC, 0xBB, 0xE0, 0x17, 0x4C, 0xC8,
|
||||
0x4E, 0x1B, 0x18, 0x75, 0x77, 0x10, 0xB2, 0x63, 0x8E, 0x5E, 0x70, 0x4E, 0xC0, 0xE5, 0xAA, 0xF5,
|
||||
0x70, 0x07, 0x91, 0xC0, 0xBD, 0x7E, 0xE6, 0x9C, 0xC0, 0xBF, 0x98, 0x7B, 0x55, 0xE4, 0x04, 0xB9,
|
||||
0x97, 0x9B, 0xA6, 0x20, 0xE6, 0xD7, 0x33, 0x59, 0xEA, 0x74, 0x51, 0x4A, 0xF8, 0x3B, 0x84, 0x8E,
|
||||
0x80, 0x5E, 0x6F, 0x9C, 0x10, 0x24, 0x1D, 0xA8, 0x24, 0x0E, 0xAA, 0xE5, 0x02, 0x45, 0x92, 0xF8,
|
||||
0x6A, 0xAB, 0x39, 0xEA, 0x96, 0x28, 0xC0, 0x49, 0xD5, 0x84, 0xCA, 0x69, 0x53, 0xF2, 0x9B, 0xAC,
|
||||
0x3F, 0x64, 0xA8, 0xB3, 0xFB, 0x79, 0xC0, 0x43, 0x3B, 0xA6, 0x12, 0xD5, 0x5B, 0x25, 0x1B, 0x8D,
|
||||
0xAC, 0xE8, 0xCA, 0x1C, 0xF5, 0x3A, 0x65, 0xD6, 0xDE, 0x01, 0x8C, 0x31, 0xEF, 0x3D, 0x5D, 0x1C,
|
||||
0x04, 0x1B, 0xE3, 0x91, 0x90, 0x9A, 0xA3, 0xA7, 0xF1, 0xF1, 0x2E, 0xA8, 0xD4, 0xCB, 0x34, 0xE5,
|
||||
0xB4, 0x39, 0xB0, 0x28, 0xE2, 0x08, 0x64, 0xEA, 0x1D, 0x09, 0x4D, 0x82, 0xCC, 0xE7, 0x05, 0xE6,
|
||||
0x36, 0x71, 0x61, 0xED, 0x80, 0x8F, 0x02, 0xBA, 0x31, 0x2A, 0x11, 0x21, 0x24, 0x35, 0x79, 0x74,
|
||||
0x30, 0x44, 0x62, 0x51, 0xBE, 0x02, 0x3C, 0x02, 0x44, 0x43, 0x9F, 0xDF, 0xE5, 0xB8, 0x31, 0x22,
|
||||
0x09, 0x29, 0x54, 0xC3, 0xF8, 0x78, 0x27, 0x54, 0x76, 0x49, 0x5F, 0x8A, 0x38, 0x12, 0x97, 0x28,
|
||||
0x85, 0x75, 0x6F, 0x09, 0x97, 0x32, 0x69, 0x77, 0xC2, 0x65, 0x86, 0xFC, 0xC5, 0x56, 0xE9, 0x2B,
|
||||
0xA6, 0x04, 0x54, 0xA2, 0xC3, 0x83, 0x85, 0x4A, 0x22, 0xCC, 0x57, 0x10, 0x2B, 0xB0, 0xFE, 0xF6,
|
||||
0x48, 0xB0, 0x79, 0xC7, 0x2F, 0xE9, 0xCC, 0xD1, 0x33, 0x5C, 0x7F, 0xCD, 0x8E, 0x76, 0x81, 0xE3,
|
||||
0x49, 0x48, 0xBD, 0x1D, 0x00, 0x89, 0x64, 0x11, 0x70, 0x34, 0x25, 0x1A, 0xE7, 0xB7, 0x84, 0xC6,
|
||||
0xF9, 0x2D, 0xA2, 0x81, 0xF0, 0x7B, 0x07, 0x2F, 0xB1, 0xB3, 0x31, 0x1C, 0x11, 0xA1, 0x39, 0xBA,
|
||||
0xBC, 0x5A, 0x78, 0x01, 0xBB, 0x5B, 0xF8, 0x25, 0xFB, 0xBE, 0x53, 0x90, 0x9C, 0xED, 0x80, 0x49,
|
||||
0x2C, 0x90, 0x8C, 0x91, 0x33, 0x89, 0xCA, 0xD9, 0x2D, 0xA1, 0x52, 0x26, 0xEB, 0x2E, 0xA8, 0x4C,
|
||||
0x11, 0x71, 0x2D, 0x4C, 0x1C, 0x76, 0xE7, 0xE2, 0xA6, 0xC0, 0x28, 0xB4, 0xE6, 0xE8, 0xFB, 0xE4,
|
||||
0xCB, 0x2E, 0xC0, 0x34, 0x77, 0xC0, 0x45, 0x95, 0x27, 0x1D, 0x2F, 0x67, 0xB0, 0x58, 0xBE, 0x25,
|
||||
0x6C, 0x5A, 0xAD, 0xDB, 0xAC, 0x2A, 0x0B, 0x6C, 0x11, 0xE4, 0xBC, 0xC7, 0x93, 0x09, 0x2C, 0x83,
|
||||
0x36, 0x2F, 0x2D, 0x29, 0x72, 0xA8, 0x2F, 0xE2, 0xBB, 0x71, 0xC9, 0xBF, 0x6F, 0xBC, 0x87, 0x91,
|
||||
0x61, 0xF7, 0xB9, 0x36, 0x32, 0x9A, 0xFA, 0xB5, 0xF0, 0x6B, 0x2F, 0x96, 0x73, 0xDB, 0x5D, 0x0D,
|
||||
0x60, 0x82, 0xA7, 0x7C, 0x53, 0x7D, 0x6B, 0x1E, 0x6D, 0xF0, 0x6C, 0x1F, 0x5D, 0xF3, 0xC7, 0x10,
|
||||
0x77, 0x59, 0x48, 0xBF, 0xC1, 0xB6, 0xF1, 0x0B, 0x71, 0xB7, 0x57, 0xA6, 0xCB, 0x04, 0xC1, 0xD8,
|
||||
0xDD, 0x8D, 0xCB, 0x19, 0x2C, 0x91, 0xE0, 0x60, 0x37, 0x26, 0x3D, 0xF0, 0x24, 0xBC, 0x20, 0xE8,
|
||||
0x4B, 0x58, 0xC4, 0xA3, 0xD5, 0x78, 0xF3, 0x82, 0xB2, 0x1A, 0x43, 0x5D, 0xFE, 0xED, 0xA9, 0x71,
|
||||
0xC9, 0x6F, 0x03, 0xDB, 0x38, 0x5D, 0x89, 0x2B, 0xD4, 0x55, 0x1C, 0x5D, 0x24, 0x2A, 0x29, 0xA7,
|
||||
0xB9, 0xB6, 0x27, 0xAA, 0x0F, 0xA0, 0xAA, 0xFB, 0xA2, 0x1A, 0xF5, 0x22, 0x01, 0xF9, 0x05, 0x3D,
|
||||
0x53, 0xD1, 0xB6, 0x9A, 0x8E, 0xB7, 0xD8, 0x8A, 0x59, 0xAB, 0xCD, 0xDB, 0x30, 0x6B, 0x05, 0x30,
|
||||
0xD9, 0x4B, 0x76, 0x87, 0xA0, 0x6D, 0x00, 0x5E, 0x7B, 0x01, 0x8A, 0xCD, 0x7A, 0x18, 0xA0, 0xB8,
|
||||
0xBE, 0x87, 0x06, 0x0A, 0xBC, 0xE5, 0x3D, 0xAB, 0xA3, 0xDB, 0x04, 0x15, 0x27, 0x34, 0x47, 0xAF,
|
||||
0x90, 0x1B, 0x42, 0x91, 0xD9, 0x17, 0x60, 0xF1, 0xC4, 0x07, 0x0B, 0x2F, 0xA9, 0xF7, 0xA1, 0xA1,
|
||||
0x03, 0x41, 0xE6, 0x9E, 0xBD, 0xF9, 0x72, 0x47, 0xD2, 0x89, 0x94, 0xF8, 0x0A, 0x8E, 0x36, 0x6E,
|
||||
0x0C, 0x22, 0x0E, 0xB7, 0xDC, 0x11, 0x88, 0xA5, 0xD4, 0xF6, 0xCD, 0xC0, 0xDB, 0xD0, 0x75, 0xAF,
|
||||
0x77, 0xE9, 0x04, 0x2E, 0x1C, 0x2F, 0xB4, 0xB7, 0xE7, 0x00, 0x6D, 0xC0, 0x8F, 0x93, 0x09, 0xB1,
|
||||
0xB6, 0x6F, 0x24, 0xA0, 0x09, 0x78, 0xE1, 0xCD, 0x2B, 0xD2, 0xDF, 0x72, 0xE1, 0xC5, 0xD6, 0x16,
|
||||
0x2B, 0x39, 0x0B, 0x50, 0xBC, 0xBC, 0xD8, 0x6B, 0xE1, 0x85, 0x39, 0x0F, 0x94, 0x19, 0x98, 0xB6,
|
||||
0x87, 0x4E, 0x0A, 0x20, 0xC4, 0x7B, 0xEE, 0x3C, 0xDB, 0x80, 0x25, 0x28, 0xE3, 0x8C, 0x1E, 0x2D,
|
||||
0xBF, 0x0F, 0xB5, 0xBE, 0x4B, 0x24, 0x4A, 0xAF, 0xEE, 0x5A, 0x67, 0x9D, 0x5E, 0xBC, 0xBC, 0xEB,
|
||||
0xB4, 0x3F, 0xEF, 0x02, 0x8F, 0x31, 0xBF, 0x5D, 0x7C, 0xDA, 0xDB, 0x40, 0x03, 0xD9, 0xE8, 0x35,
|
||||
0xBB, 0xCE, 0xB0, 0x41, 0xC2, 0xDE, 0x3D, 0x90, 0xDA, 0x87, 0x8B, 0xA4, 0xF6, 0x17, 0x10, 0x4A,
|
||||
0xD3, 0x2D, 0x32, 0xDE, 0x94, 0x65, 0xBC, 0xEF, 0x2F, 0xF6, 0x83, 0xD0, 0xF4, 0x60, 0xA9, 0x6E,
|
||||
0x7A, 0xD0, 0x54, 0x67, 0x88, 0x9B, 0xAD, 0x62, 0x98, 0xB6, 0xEC, 0x60, 0x25, 0xA1, 0xD8, 0xCB,
|
||||
0xDA, 0x25, 0xC9, 0xB5, 0xAE, 0x76, 0xC9, 0x72, 0x91, 0x18, 0xE9, 0x24, 0xD7, 0x4B, 0xAE, 0x8A,
|
||||
0x9C, 0x7D, 0xDE, 0xCB, 0xBA, 0xDD, 0x32, 0x69, 0x77, 0x09, 0x1A, 0x1F, 0xAD, 0xDE, 0x4F, 0xE7,
|
||||
0x68, 0x63, 0x30, 0x24, 0x1D, 0x60, 0xF1, 0xEA, 0xC9, 0x3E, 0xDB, 0x85, 0x68, 0xDE, 0xC3, 0xC4,
|
||||
0x51, 0xAC, 0xF5, 0xA1, 0x73, 0x9D, 0x83, 0xDD, 0xCD, 0x93, 0x1D, 0x23, 0x32, 0x47, 0x2F, 0xB1,
|
||||
0x1B, 0x18, 0x17, 0x9E, 0x2F, 0xDF, 0xFD, 0xB4, 0x17, 0xD4, 0xF8, 0xCC, 0x87, 0x81, 0x4C, 0x28,
|
||||
0x7D, 0x68, 0xBC, 0x66, 0x73, 0xE2, 0xFB, 0x9E, 0xBF, 0x31, 0x64, 0x92, 0x0E, 0x96, 0x15, 0xF5,
|
||||
0x57, 0xFC, 0x68, 0x2F, 0x70, 0x45, 0xB3, 0x1E, 0x06, 0xB1, 0x58, 0xE7, 0x43, 0x83, 0xB6, 0x9C,
|
||||
0x38, 0x64, 0xB1, 0x31, 0x64, 0x9C, 0xCA, 0x1C, 0xBD, 0xAB, 0x3F, 0x87, 0x7F, 0xF7, 0x02, 0x97,
|
||||
0x98, 0xF1, 0x30, 0x60, 0x49, 0x6D, 0x0F, 0x0D, 0xD5, 0x78, 0xB1, 0x79, 0x3A, 0x04, 0x1A, 0x73,
|
||||
0xF4, 0xF4, 0xA7, 0xFD, 0xF4, 0x7E, 0x6C, 0xB2, 0x8A, 0x08, 0xED, 0x84, 0x07, 0x57, 0xEA, 0xD0,
|
||||
0x68, 0xAC, 0xB6, 0x40, 0x63, 0xC5, 0x04, 0xFF, 0x6D, 0x4F, 0x68, 0xAC, 0xAA, 0xA3, 0xF1, 0x99,
|
||||
0xE3, 0x65, 0xF5, 0x25, 0xE0, 0xC3, 0x9F, 0xC5, 0x18, 0xA3, 0xCD, 0xCB, 0x51, 0x44, 0xC8, 0x6E,
|
||||
0x1A, 0x83, 0x23, 0xE3, 0x29, 0xDA, 0x4F, 0x41, 0x8A, 0xE7, 0xDD, 0x47, 0x08, 0x25, 0x4A, 0x1E,
|
||||
0x1A, 0xA7, 0x09, 0xB2, 0xF0, 0x7B, 0x1B, 0xD3, 0x6D, 0xAE, 0x2D, 0x2B, 0xB4, 0xE6, 0xE8, 0x39,
|
||||
0x7C, 0x31, 0x9E, 0xF1, 0x2F, 0xFB, 0x6A, 0xF9, 0xD4, 0xF9, 0xF7, 0x81, 0x5A, 0x4A, 0xDF, 0x2F,
|
||||
0x02, 0x38, 0x68, 0xB0, 0xBD, 0xA9, 0xBB, 0xD5, 0x23, 0x0D, 0x29, 0x72, 0x09, 0xDF, 0x1B, 0xF1,
|
||||
0x7D, 0xBF, 0x00, 0x26, 0x42, 0xEC, 0x0D, 0x43, 0x45, 0xEF, 0x7D, 0xC0, 0x18, 0x3D, 0x16, 0xC4,
|
||||
0x8B, 0xB4, 0x78, 0x15, 0x5E, 0x19, 0x52, 0xF2, 0xE1, 0x27, 0x7E, 0x4B, 0x0B, 0xA6, 0xF5, 0x80,
|
||||
0x12, 0xC7, 0x81, 0x85, 0x30, 0xA6, 0xC6, 0x5B, 0x76, 0x38, 0x38, 0x15, 0x03, 0xAA, 0x73, 0x91,
|
||||
0xCF, 0xDC, 0xB0, 0x97, 0x50, 0xA2, 0xB9, 0x39, 0x7A, 0xCB, 0x5E, 0x12, 0x08, 0xBC, 0xD8, 0xB7,
|
||||
0xCD, 0x99, 0x71, 0x23, 0x62, 0xD7, 0xF7, 0x40, 0xA8, 0x18, 0x24, 0xF9, 0xAE, 0x26, 0xD3, 0x88,
|
||||
0x8E, 0x94, 0xDF, 0x46, 0x97, 0x7C, 0xB0, 0xC1, 0xBC, 0xAC, 0x7C, 0x3A, 0x76, 0xD5, 0xC2, 0xCA,
|
||||
0xBF, 0xB8, 0x31, 0x38, 0x75, 0x91, 0xC6, 0xDC, 0x39, 0x28, 0x0C, 0xC4, 0xDB, 0x25, 0x73, 0x58,
|
||||
0xC5, 0xCF, 0x33, 0x71, 0x4B, 0x24, 0x8F, 0x69, 0xC6, 0x6A, 0x65, 0x1F, 0xDF, 0x94, 0xDB, 0x4C,
|
||||
0xD5, 0x82, 0x96, 0x3F, 0x88, 0x29, 0xEB, 0x21, 0x3B, 0x8C, 0xCD, 0xFF, 0x9F, 0x7F, 0x97, 0xF9,
|
||||
0x0C, 0x7B, 0xF7, 0x67, 0x22, 0x98, 0x69, 0x04, 0xBE, 0x35, 0x34, 0xF3, 0x9E, 0x8E, 0xCA, 0xD1,
|
||||
0xFC, 0x54, 0xA7, 0x7A, 0x66, 0xB0, 0xC6, 0xD6, 0x83, 0xC0, 0xF2, 0xC9, 0x82, 0x8E, 0xEE, 0xD8,
|
||||
0x9E, 0x15, 0xCE, 0xB1, 0x4B, 0x1B, 0xC8, 0xB6, 0x2F, 0x97, 0x70, 0xF0, 0x92, 0x04, 0x14, 0x83,
|
||||
0x15, 0x6A, 0x47, 0xCF, 0x7E, 0x7C, 0x75, 0x21, 0x9E, 0x12, 0x7B, 0xE9, 0x21, 0x1B, 0xDB, 0x47,
|
||||
0x27, 0xC6, 0x24, 0x74, 0x85, 0x9B, 0xD7, 0x30, 0x1B, 0x2B, 0xDE, 0xBB, 0xBA, 0x44, 0xBE, 0x31,
|
||||
0x46, 0x01, 0x7E, 0xE1, 0x05, 0xD4, 0x18, 0x1A, 0x31, 0x47, 0xC7, 0xB3, 0xF8, 0x7D, 0xBF, 0x0D,
|
||||
0xCF, 0x27, 0x53, 0xE2, 0xCA, 0x91, 0x42, 0xD9, 0x5F, 0x7D, 0x07, 0x86, 0xC6, 0x54, 0xDF, 0x1A,
|
||||
0x47, 0xFD, 0xF3, 0xD6, 0x11, 0x7B, 0x1C, 0x0F, 0x60, 0x80, 0x1F, 0x00, 0x02, 0x0C, 0x03, 0x20,
|
||||
0xC0, 0x87, 0x23, 0xF9, 0x78, 0x20, 0x76, 0x1A, 0xDC, 0xE4, 0x4C, 0x40, 0x26, 0x6D, 0xED, 0x48,
|
||||
0xE0, 0x74, 0xC4, 0x1E, 0x34, 0xBE, 0x89, 0x29, 0x83, 0x99, 0xB7, 0x2A, 0xA2, 0xF4, 0xF1, 0xDC,
|
||||
0x5B, 0xE2, 0x0C, 0x71, 0x4C, 0x2D, 0xBD, 0xB9, 0x74, 0xEA, 0xC8, 0xEB, 0x8F, 0x8E, 0xA3, 0x01,
|
||||
0xF1, 0x7B, 0xCC, 0x86, 0x06, 0xF5, 0x43, 0x9C, 0x66, 0x8B, 0xDD, 0x32, 0xAE, 0x91, 0x58, 0x85,
|
||||
0x8C, 0x27, 0xC8, 0x09, 0x32, 0x9C, 0xC3, 0x85, 0x8D, 0x28, 0x7E, 0xC7, 0x76, 0x0C, 0x61, 0x40,
|
||||
0x0D, 0x3B, 0x27, 0x62, 0xFB, 0xF0, 0x44, 0x9E, 0x79, 0x03, 0x7C, 0x29, 0x3E, 0x4E, 0x66, 0x55,
|
||||
0x7F, 0x06, 0x8A, 0xF4, 0xD7, 0xA1, 0xE1, 0x86, 0x10, 0xC2, 0x8F, 0xB9, 0x0A, 0x46, 0x3F, 0x75,
|
||||
0x96, 0x53, 0x3B, 0x90, 0x9D, 0xE4, 0x3B, 0xDB, 0xF9, 0x9C, 0xFC, 0x47, 0x32, 0x61, 0x13, 0x37,
|
||||
0xF8, 0x1B, 0xE4, 0x87, 0xC0, 0xE3, 0x28, 0xCA, 0xEE, 0x47, 0xC9, 0x8B, 0x79, 0x55, 0x22, 0x6E,
|
||||
0x87, 0x86, 0xEC, 0x83, 0xE5, 0xF9, 0xA5, 0x3C, 0x71, 0xF7, 0xEE, 0x32, 0xE6, 0x6B, 0x28, 0xC3,
|
||||
0xE0, 0x54, 0x72, 0xE2, 0x06, 0x4E, 0x28, 0x4F, 0x3F, 0xAF, 0xF3, 0xCE, 0xF0, 0x88, 0x98, 0x2B,
|
||||
0x1C, 0xEE, 0xC4, 0x92, 0xA7, 0x2C, 0xF0, 0xE0, 0x41, 0x9A, 0xDB, 0xDD, 0xA1, 0xA4, 0x4A, 0x34,
|
||||
0x11, 0xE3, 0x21, 0x32, 0x20, 0xF2, 0x40, 0x6D, 0xF9, 0x4C, 0xBC, 0x14, 0x89, 0x4C, 0x6A, 0x77,
|
||||
0x53, 0x86, 0x8F, 0x65, 0x9C, 0x30, 0x13, 0x11, 0x9B, 0x1B, 0x88, 0x5F, 0x33, 0x3C, 0x4E, 0x9E,
|
||||
0x7A, 0x15, 0xF2, 0x3D, 0xE6, 0x5E, 0x5F, 0xC3, 0xF2, 0xF2, 0xDB, 0x31, 0xD8, 0x9F, 0x39, 0x73,
|
||||
0xF2, 0x83, 0x1C, 0x9F, 0x4C, 0xA5, 0x72, 0x9C, 0xA6, 0x38, 0x32, 0xC5, 0x32, 0x72, 0xB3, 0x0F,
|
||||
0x9F, 0x00, 0x86, 0xB2, 0x9D, 0xEF, 0xE4, 0xF9, 0xFC, 0x8C, 0x39, 0xD9, 0x87, 0x4F, 0xBC, 0x3E,
|
||||
0xB0, 0x50, 0x82, 0xE8, 0x0E, 0x09, 0x8D, 0x62, 0x9C, 0xDD, 0x6A, 0xCC, 0x54, 0xE2, 0x22, 0xC0,
|
||||
0x61, 0x11, 0xAB, 0x4C, 0x01, 0xD7, 0x30, 0x14, 0x01, 0x55, 0x13, 0xF5, 0xE9, 0x29, 0xAF, 0x35,
|
||||
0x8C, 0xB9, 0x8C, 0x95, 0xF4, 0xEF, 0x77, 0x54, 0xE1, 0x6F, 0xA2, 0xF0, 0x89, 0x53, 0x99, 0x8A,
|
||||
0x27, 0xF3, 0xE3, 0xC8, 0x62, 0xCC, 0xD5, 0x13, 0x87, 0x91, 0xAF, 0x2B, 0x89, 0xFC, 0x3C, 0x31,
|
||||
0xAB, 0x05, 0x39, 0x4C, 0xF1, 0xF8, 0x7E, 0x46, 0x54, 0xD5, 0xD5, 0x41, 0xEE, 0x96, 0xA1, 0xBE,
|
||||
0x80, 0x64, 0x0C, 0xA9, 0xF0, 0x63, 0x8A, 0x0F, 0xDF, 0xB0, 0x8F, 0x99, 0x88, 0xDF, 0xC4, 0xE5,
|
||||
0xFD, 0xBA, 0xE7, 0x62, 0x3D, 0x77, 0xD5, 0xD9, 0x75, 0x3C, 0x45, 0x29, 0xCE, 0x32, 0x0D, 0xC7,
|
||||
0x73, 0x42, 0x35, 0x0C, 0x8F, 0x20, 0x0D, 0xEB, 0x78, 0xC9, 0x06, 0x2D, 0x21, 0xF0, 0x31, 0x0D,
|
||||
0x7D, 0x57, 0x8D, 0x26, 0x91, 0x91, 0xFE, 0x0E, 0xB1, 0x7F, 0x0D, 0x8C, 0x3E, 0xDC, 0xFF, 0x14,
|
||||
0xE5, 0xF7, 0x9B, 0x53, 0xFE, 0x6C, 0x8E, 0xE7, 0x3C, 0x86, 0x0A, 0x30, 0xBC, 0xFF, 0x89, 0x43,
|
||||
0x7D, 0xF3, 0x00, 0xA6, 0x84, 0x2F, 0x7C, 0xE2, 0x9B, 0x0F, 0x82, 0xC5, 0x84, 0xBD, 0x3E, 0xBB,
|
||||
0xC6, 0x59, 0x44, 0xB8, 0x35, 0xE8, 0x0C, 0xBB, 0x35, 0x1F, 0x07, 0x0B, 0x60, 0x8F, 0x93, 0x44,
|
||||
0x16, 0xCD, 0xE8, 0x39, 0x18, 0x4A, 0xCD, 0xB4, 0xF6, 0xC1, 0xC7, 0x40, 0x07, 0x02, 0x50, 0xCF,
|
||||
0xB8, 0xFF, 0x89, 0xB3, 0xB8, 0x31, 0x26, 0x10, 0xCD, 0xC1, 0x0C, 0xDB, 0x27, 0x50, 0x77, 0x10,
|
||||
0x65, 0x4F, 0xA6, 0xDF, 0xFF, 0x14, 0xB1, 0x6A, 0x88, 0x9F, 0x6E, 0x3E, 0xC4, 0x1E, 0x12, 0x17,
|
||||
0x83, 0xA8, 0x86, 0xF1, 0x13, 0x0D, 0xCE, 0xEB, 0x2D, 0x47, 0xC1, 0xF3, 0x9F, 0x38, 0x4E, 0xED,
|
||||
0x48, 0xBC, 0x7E, 0x41, 0xE6, 0xE8, 0x06, 0x34, 0x9D, 0x97, 0x08, 0xC4, 0x56, 0x93, 0x3B, 0xCF,
|
||||
0x3B, 0x9E, 0x6B, 0x39, 0xC4, 0xFA, 0xC8, 0x12, 0xF3, 0x71, 0x5A, 0x70, 0x11, 0xE9, 0x4E, 0x43,
|
||||
0xBC, 0x4E, 0xEB, 0xB5, 0x67, 0xE3, 0x8C, 0x9B, 0x1E, 0x33, 0x31, 0x4E, 0x4F, 0xC1, 0xCA, 0xC8,
|
||||
0x8E, 0x52, 0x92, 0xC0, 0x88, 0xBD, 0x77, 0x45, 0x98, 0x29, 0x65, 0x61, 0xA1, 0x8C, 0xD4, 0x45,
|
||||
0xD8, 0x2C, 0xA9, 0xD6, 0x91, 0xCA, 0x89, 0xDB, 0x0A, 0xF4, 0x8C, 0xD8, 0x16, 0x7F, 0x05, 0x9E,
|
||||
0x5B, 0x3B, 0xBE, 0x13, 0x9B, 0x61, 0x9D, 0x07, 0x9B, 0x40, 0x61, 0x90, 0x32, 0x51, 0x9E, 0x99,
|
||||
0xD2, 0x5D, 0xFD, 0x51, 0x92, 0x49, 0x72, 0x6C, 0x26, 0x3E, 0x4A, 0x4D, 0xE3, 0x05, 0x8D, 0xCF,
|
||||
0xFC, 0x07, 0x77, 0x9A, 0x3F, 0x4F, 0x44, 0x11, 0x54, 0x72, 0xD2, 0xB1, 0x62, 0x30, 0xE1, 0x81,
|
||||
0xEC, 0xBF, 0x1E, 0x51, 0x1B, 0x11, 0xE8, 0xAE, 0x2F, 0x1D, 0xCC, 0x0E, 0x9F, 0x5E, 0xFF, 0x00,
|
||||
0xC5, 0x5B, 0xB4, 0x20, 0x5C, 0x9A, 0x84, 0xE0, 0x22, 0x6E, 0xFF, 0x4A, 0x29, 0x93, 0x56, 0x51,
|
||||
0xE1, 0xC1, 0xDB, 0x77, 0x91, 0x71, 0x8A, 0x38, 0xC4, 0x9D, 0x7E, 0x8A, 0x94, 0x71, 0x2D, 0xA7,
|
||||
0x4D, 0xF5, 0xF7, 0x0A, 0xBD, 0x9A, 0xED, 0x8A, 0xE8, 0x95, 0x96, 0x5E, 0xA1, 0xE6, 0xAE, 0x5C,
|
||||
0x4E, 0xAC, 0x36, 0xB7, 0x47, 0x8A, 0xB1, 0x03, 0xEA, 0x2D, 0xC4, 0x1A, 0x23, 0xE3, 0xE6, 0x2B,
|
||||
0xE2, 0xDA, 0xDE, 0xAA, 0xC1, 0xCE, 0xD7, 0x64, 0x91, 0x54, 0x15, 0x6D, 0x10, 0x17, 0x0C, 0xF8,
|
||||
0xE2, 0x97, 0x57, 0x2F, 0x59, 0xD2, 0x51, 0xD7, 0x2A, 0x47, 0xE9, 0x0E, 0x87, 0xBF, 0xEB, 0x5C,
|
||||
0x3B, 0x03, 0x83, 0xAD, 0x01, 0x4D, 0xB3, 0x48, 0x36, 0x71, 0x63, 0xC9, 0x62, 0x81, 0x1D, 0x7E,
|
||||
0x10, 0x73, 0xB2, 0xD2, 0x93, 0x02, 0xF8, 0xB8, 0x54, 0x16, 0x6F, 0x91, 0x15, 0x05, 0x22, 0xF1,
|
||||
0x09, 0xA5, 0xE0, 0xB0, 0x86, 0x70, 0xE5, 0x80, 0x65, 0x19, 0xB9, 0xCE, 0xBB, 0x63, 0xA8, 0xE0,
|
||||
0xE7, 0x04, 0x7D, 0x62, 0x26, 0x19, 0x65, 0x69, 0xE1, 0x95, 0x4C, 0x89, 0x16, 0x10, 0x99, 0xF8,
|
||||
0xF1, 0x7B, 0x6B, 0x0C, 0xC9, 0xF1, 0x19, 0x78, 0x7E, 0xC3, 0x05, 0x0D, 0x8E, 0x6F, 0x8A, 0xD4,
|
||||
0x11, 0xE6, 0x4A, 0x80, 0xAC, 0x2A, 0x04, 0x4F, 0x43, 0x7A, 0x6E, 0x29, 0xFB, 0xE8, 0xD9, 0xA9,
|
||||
0xDE, 0x2B, 0xAE, 0xDD, 0xB2, 0x36, 0x2D, 0xCF, 0xB0, 0xC3, 0x75, 0xD3, 0x8A, 0x3E, 0x25, 0xC5,
|
||||
0x20, 0x49, 0x30, 0x6B, 0xC2, 0x66, 0xDA, 0x14, 0xC5, 0x2F, 0xA2, 0x01, 0x91, 0xEC, 0x6A, 0x40,
|
||||
0xE4, 0xC8, 0x9E, 0xEE, 0xE2, 0x32, 0xED, 0x42, 0x06, 0x72, 0x99, 0xC5, 0x0C, 0xF6, 0xD6, 0x8F,
|
||||
0x19, 0x2B, 0xD0, 0xD2, 0x09, 0xAA, 0x14, 0x0A, 0x6D, 0x06, 0x2C, 0xAC, 0x18, 0x62, 0x86, 0x48,
|
||||
0xDA, 0x6C, 0xB7, 0x99, 0xAE, 0x0E, 0x17, 0x21, 0x58, 0x69, 0x1E, 0xF9, 0xA4, 0xF8, 0x8D, 0xB5,
|
||||
0x6C, 0x71, 0xF0, 0x40, 0x0B, 0x57, 0x14, 0xD4, 0x70, 0x5A, 0xC9, 0x04, 0xB2, 0xDF, 0x2B, 0x21,
|
||||
0x50, 0xEE, 0xBA, 0xE0, 0xB4, 0xF0, 0xD3, 0xBA, 0xD8, 0x1A, 0x23, 0xC3, 0xB8, 0xE3, 0x18, 0x73,
|
||||
0x46, 0x24, 0xBB, 0xA2, 0x04, 0xF1, 0xF5, 0xEE, 0x34, 0x0B, 0xF9, 0x5A, 0x57, 0x7A, 0xA3, 0xA0,
|
||||
0x15, 0xDD, 0xB7, 0x96, 0xE8, 0x83, 0x8B, 0x95, 0xC7, 0xAA, 0xF2, 0x51, 0x97, 0x5D, 0x42, 0xA1,
|
||||
0xDE, 0x65, 0x27, 0xD4, 0xC7, 0x15, 0xD5, 0xC7, 0x52, 0x7D, 0x46, 0x90, 0x34, 0x84, 0xE5, 0x2D,
|
||||
0x7F, 0xEC, 0x8C, 0xBF, 0x3D, 0x4D, 0x34, 0x5B, 0x8D, 0x0B, 0xE5, 0x94, 0xAD, 0xB8, 0xA2, 0x5E,
|
||||
0x31, 0x41, 0xEA, 0x9E, 0x62, 0xA1, 0xD6, 0x6A, 0x5C, 0x4D, 0xAD, 0xA8, 0x95, 0x67, 0x04, 0x89,
|
||||
0x5A, 0xFA, 0x86, 0x3F, 0x52, 0x25, 0xDE, 0x42, 0xE6, 0xFF, 0xA7, 0x4B, 0xFC, 0xCE, 0x94, 0x58,
|
||||
0x58, 0xB1, 0xFF, 0x5A, 0x5A, 0xCA, 0xC4, 0x30, 0x45, 0xC9, 0x78, 0xC9, 0x50, 0x4A, 0x1A, 0x8F,
|
||||
0x54, 0xA8, 0x63, 0x39, 0x0A, 0xA9, 0xA3, 0x41, 0xA2, 0x06, 0xC6, 0x5F, 0x2B, 0x19, 0x2B, 0x1E,
|
||||
0x9D, 0x04, 0x42, 0xC2, 0x40, 0x34, 0xE0, 0x23, 0xE3, 0x2C, 0xBB, 0xD4, 0x14, 0x8D, 0x90, 0x50,
|
||||
0x36, 0xD3, 0xFE, 0xA8, 0x03, 0x62, 0x95, 0x52, 0x63, 0xE2, 0x00, 0x11, 0xF4, 0x79, 0x62, 0x96,
|
||||
0x8A, 0x82, 0x1C, 0xEC, 0xD3, 0x9A, 0xF9, 0x93, 0x83, 0xD9, 0xF2, 0x41, 0xDE, 0x14, 0x7E, 0xF1,
|
||||
0xC3, 0x73, 0xC3, 0xF3, 0x0D, 0xF1, 0x16, 0x4D, 0x3F, 0x7E, 0x6B, 0x8E, 0x21, 0x5F, 0x31, 0xC7,
|
||||
0x17, 0x69, 0xC4, 0x9D, 0x1A, 0x74, 0x46, 0x02, 0xE8, 0x59, 0xD9, 0x93, 0xE0, 0xF8, 0xAE, 0x19,
|
||||
0xBF, 0x45, 0xAE, 0x54, 0x3D, 0xD1, 0xA4, 0x7E, 0x17, 0x2B, 0x92, 0x31, 0xA7, 0xA0, 0x49, 0x6C,
|
||||
0x79, 0x57, 0xEA, 0xB8, 0x96, 0x58, 0x8A, 0x96, 0x85, 0x1B, 0x98, 0x30, 0x3E, 0xFD, 0xC5, 0x5A,
|
||||
0x51, 0xAF, 0x40, 0xA9, 0x21, 0x63, 0xB2, 0xC4, 0x96, 0x89, 0xAE, 0x6B, 0xD6, 0xD4, 0xAD, 0xBD,
|
||||
0x0B, 0x10, 0x65, 0x5B, 0x49, 0xDA, 0x6C, 0x9E, 0x8F, 0x8A, 0xB0, 0xB8, 0xA8, 0x72, 0xE2, 0x33,
|
||||
0x38, 0x8D, 0x36, 0x2C, 0xC5, 0x37, 0xF1, 0x52, 0xAE, 0xC1, 0xA9, 0xF8, 0x9F, 0x0A, 0xFF, 0x0B,
|
||||
0x9B, 0xFC, 0x8E, 0x51, 0xC1, 0x70, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,99 @@
|
||||
|
||||
#if defined(CAMERA_MODEL_WROVER_KIT)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 21
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 19
|
||||
#define Y4_GPIO_NUM 18
|
||||
#define Y3_GPIO_NUM 5
|
||||
#define Y2_GPIO_NUM 4
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#elif defined(CAMERA_MODEL_ESP_EYE)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 4
|
||||
#define SIOD_GPIO_NUM 18
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 36
|
||||
#define Y8_GPIO_NUM 37
|
||||
#define Y7_GPIO_NUM 38
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 35
|
||||
#define Y4_GPIO_NUM 14
|
||||
#define Y3_GPIO_NUM 13
|
||||
#define Y2_GPIO_NUM 34
|
||||
#define VSYNC_GPIO_NUM 5
|
||||
#define HREF_GPIO_NUM 27
|
||||
#define PCLK_GPIO_NUM 25
|
||||
|
||||
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM 15
|
||||
#define XCLK_GPIO_NUM 27
|
||||
#define SIOD_GPIO_NUM 25
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 19
|
||||
#define Y8_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 5
|
||||
#define Y4_GPIO_NUM 34
|
||||
#define Y3_GPIO_NUM 35
|
||||
#define Y2_GPIO_NUM 32
|
||||
#define VSYNC_GPIO_NUM 22
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_M5STACK_WIDE)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM 15
|
||||
#define XCLK_GPIO_NUM 27
|
||||
#define SIOD_GPIO_NUM 22
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 19
|
||||
#define Y8_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 5
|
||||
#define Y4_GPIO_NUM 34
|
||||
#define Y3_GPIO_NUM 35
|
||||
#define Y2_GPIO_NUM 32
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 0
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 21
|
||||
#define Y4_GPIO_NUM 19
|
||||
#define Y3_GPIO_NUM 18
|
||||
#define Y2_GPIO_NUM 5
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#else
|
||||
#error "Camera model not selected"
|
||||
#endif
|
@ -22,7 +22,7 @@ compiler.warning_flags.all=-Wall -Werror=all -Wextra
|
||||
|
||||
compiler.path={runtime.tools.xtensa-esp32-elf-gcc.path}/bin/
|
||||
compiler.sdk.path={runtime.platform.path}/tools/sdk
|
||||
compiler.cpreprocessor.flags=-DESP_PLATFORM -DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h" -DHAVE_CONFIG_H "-I{compiler.sdk.path}/include/config" "-I{compiler.sdk.path}/include/app_trace" "-I{compiler.sdk.path}/include/app_update" "-I{compiler.sdk.path}/include/asio" "-I{compiler.sdk.path}/include/bootloader_support" "-I{compiler.sdk.path}/include/bt" "-I{compiler.sdk.path}/include/coap" "-I{compiler.sdk.path}/include/console" "-I{compiler.sdk.path}/include/driver" "-I{compiler.sdk.path}/include/efuse" "-I{compiler.sdk.path}/include/esp-tls" "-I{compiler.sdk.path}/include/esp32" "-I{compiler.sdk.path}/include/esp_adc_cal" "-I{compiler.sdk.path}/include/esp_event" "-I{compiler.sdk.path}/include/esp_http_client" "-I{compiler.sdk.path}/include/esp_http_server" "-I{compiler.sdk.path}/include/esp_https_ota" "-I{compiler.sdk.path}/include/esp_https_server" "-I{compiler.sdk.path}/include/esp_ringbuf" "-I{compiler.sdk.path}/include/espcoredump" "-I{compiler.sdk.path}/include/ethernet" "-I{compiler.sdk.path}/include/expat" "-I{compiler.sdk.path}/include/fatfs" "-I{compiler.sdk.path}/include/freemodbus" "-I{compiler.sdk.path}/include/freertos" "-I{compiler.sdk.path}/include/heap" "-I{compiler.sdk.path}/include/idf_test" "-I{compiler.sdk.path}/include/jsmn" "-I{compiler.sdk.path}/include/json" "-I{compiler.sdk.path}/include/libsodium" "-I{compiler.sdk.path}/include/log" "-I{compiler.sdk.path}/include/lwip" "-I{compiler.sdk.path}/include/mbedtls" "-I{compiler.sdk.path}/include/mdns" "-I{compiler.sdk.path}/include/micro-ecc" "-I{compiler.sdk.path}/include/mqtt" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/nghttp" "-I{compiler.sdk.path}/include/nvs_flash" "-I{compiler.sdk.path}/include/openssl" "-I{compiler.sdk.path}/include/protobuf-c" "-I{compiler.sdk.path}/include/protocomm" "-I{compiler.sdk.path}/include/pthread" "-I{compiler.sdk.path}/include/sdmmc" "-I{compiler.sdk.path}/include/smartconfig_ack" "-I{compiler.sdk.path}/include/soc" "-I{compiler.sdk.path}/include/spi_flash" "-I{compiler.sdk.path}/include/spiffs" "-I{compiler.sdk.path}/include/tcp_transport" "-I{compiler.sdk.path}/include/tcpip_adapter" "-I{compiler.sdk.path}/include/ulp" "-I{compiler.sdk.path}/include/unity" "-I{compiler.sdk.path}/include/vfs" "-I{compiler.sdk.path}/include/wear_levelling" "-I{compiler.sdk.path}/include/wifi_provisioning" "-I{compiler.sdk.path}/include/wpa_supplicant" "-I{compiler.sdk.path}/include/xtensa-debug-module" "-I{compiler.sdk.path}/include/esp32-camera" "-I{compiler.sdk.path}/include/esp-face" "-I{compiler.sdk.path}/include/fb_gfx"
|
||||
compiler.cpreprocessor.flags=-DESP_PLATFORM -DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h" -DHAVE_CONFIG_H "-I{compiler.sdk.path}/include/config" "-I{compiler.sdk.path}/include/app_trace" "-I{compiler.sdk.path}/include/app_update" "-I{compiler.sdk.path}/include/asio" "-I{compiler.sdk.path}/include/bootloader_support" "-I{compiler.sdk.path}/include/bt" "-I{compiler.sdk.path}/include/coap" "-I{compiler.sdk.path}/include/console" "-I{compiler.sdk.path}/include/driver" "-I{compiler.sdk.path}/include/esp-tls" "-I{compiler.sdk.path}/include/esp32" "-I{compiler.sdk.path}/include/esp_adc_cal" "-I{compiler.sdk.path}/include/esp_event" "-I{compiler.sdk.path}/include/esp_http_client" "-I{compiler.sdk.path}/include/esp_http_server" "-I{compiler.sdk.path}/include/esp_https_ota" "-I{compiler.sdk.path}/include/esp_ringbuf" "-I{compiler.sdk.path}/include/ethernet" "-I{compiler.sdk.path}/include/expat" "-I{compiler.sdk.path}/include/fatfs" "-I{compiler.sdk.path}/include/freemodbus" "-I{compiler.sdk.path}/include/freertos" "-I{compiler.sdk.path}/include/heap" "-I{compiler.sdk.path}/include/idf_test" "-I{compiler.sdk.path}/include/jsmn" "-I{compiler.sdk.path}/include/json" "-I{compiler.sdk.path}/include/libsodium" "-I{compiler.sdk.path}/include/log" "-I{compiler.sdk.path}/include/lwip" "-I{compiler.sdk.path}/include/mbedtls" "-I{compiler.sdk.path}/include/mdns" "-I{compiler.sdk.path}/include/micro-ecc" "-I{compiler.sdk.path}/include/mqtt" "-I{compiler.sdk.path}/include/newlib" "-I{compiler.sdk.path}/include/nghttp" "-I{compiler.sdk.path}/include/nvs_flash" "-I{compiler.sdk.path}/include/openssl" "-I{compiler.sdk.path}/include/protobuf-c" "-I{compiler.sdk.path}/include/protocomm" "-I{compiler.sdk.path}/include/pthread" "-I{compiler.sdk.path}/include/sdmmc" "-I{compiler.sdk.path}/include/smartconfig_ack" "-I{compiler.sdk.path}/include/soc" "-I{compiler.sdk.path}/include/spi_flash" "-I{compiler.sdk.path}/include/spiffs" "-I{compiler.sdk.path}/include/tcp_transport" "-I{compiler.sdk.path}/include/tcpip_adapter" "-I{compiler.sdk.path}/include/ulp" "-I{compiler.sdk.path}/include/vfs" "-I{compiler.sdk.path}/include/wear_levelling" "-I{compiler.sdk.path}/include/wifi_provisioning" "-I{compiler.sdk.path}/include/wpa_supplicant" "-I{compiler.sdk.path}/include/xtensa-debug-module" "-I{compiler.sdk.path}/include/esp32-camera" "-I{compiler.sdk.path}/include/esp-face" "-I{compiler.sdk.path}/include/fb_gfx"
|
||||
|
||||
compiler.c.cmd=xtensa-esp32-elf-gcc
|
||||
compiler.c.flags=-std=gnu99 -Os -g3 -fstack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wpointer-arith {compiler.warning_flags} -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -Wno-old-style-declaration -MMD -c
|
||||
@ -35,7 +35,7 @@ compiler.S.flags=-c -g3 -x assembler-with-cpp -MMD -mlongcalls
|
||||
|
||||
compiler.c.elf.cmd=xtensa-esp32-elf-gcc
|
||||
compiler.c.elf.flags=-nostdlib "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld -T esp32.rom.spiram_incompatible_fns.ld -u ld_include_panic_highint_hdl -u call_user_start_cpu0 -Wl,--gc-sections -Wl,-static -Wl,--undefined=uxTopUsedPriority -u __cxa_guard_dummy -u __cxx_fatal_exception
|
||||
compiler.c.elf.libs=-lgcc -lopenssl -lbtdm_app -lfatfs -lwps -lcoexist -lwear_levelling -lesp_http_client -lprotobuf-c -lhal -lnewlib -ldriver -lbootloader_support -lpp -lfreemodbus -lmesh -lsmartconfig -ljsmn -lwpa -lethernet -lphy -lfrmn -lapp_trace -lfr_coefficients -lconsole -lulp -lwpa_supplicant -lfreertos -lbt -lmicro-ecc -lesp32-camera -lcxx -lxtensa-debug-module -ltcp_transport -lmdns -lvfs -lmtmn -lespcoredump -lesp_ringbuf -lsoc -lcore -lfb_gfx -lsdmmc -llibsodium -lcoap -ltcpip_adapter -lprotocomm -lesp_event -limage_util -lc_nano -lesp-tls -lasio -lrtc -lspi_flash -lwpa2 -lwifi_provisioning -lesp32 -lface_recognition -lapp_update -lnghttp -lspiffs -lface_detection -lefuse -lunity -lesp_https_server -lespnow -lnvs_flash -lesp_adc_cal -llog -ldl_lib -lsmartconfig_ack -lexpat -lfd_coefficients -lm -lmqtt -lc -lheap -lmbedtls -llwip -lnet80211 -lesp_http_server -lpthread -ljson -lesp_https_ota -lstdc++
|
||||
compiler.c.elf.libs=-lgcc -lopenssl -lbtdm_app -lfatfs -lwps -lcoexist -lwear_levelling -lesp_http_client -lprotobuf-c -lhal -lnewlib -ldriver -lbootloader_support -lpp -lfreemodbus -lmesh -lsmartconfig -ljsmn -lwpa -lethernet -lphy -lfrmn -lapp_trace -lfr_coefficients -lconsole -lulp -lwpa_supplicant -lfreertos -lbt -lmicro-ecc -lesp32-camera -lcxx -lxtensa-debug-module -ltcp_transport -lmdns -lvfs -lmtmn -lesp_ringbuf -lsoc -lcore -lfb_gfx -lsdmmc -llibsodium -lcoap -ltcpip_adapter -lprotocomm -lesp_event -limage_util -lc_nano -lesp-tls -lasio -lrtc -lspi_flash -lwpa2 -lwifi_provisioning -lesp32 -lface_recognition -lapp_update -lnghttp -lspiffs -lface_detection -lespnow -lnvs_flash -lesp_adc_cal -llog -ldl_lib -lsmartconfig_ack -lexpat -lfd_coefficients -lm -lmqtt -lc -lheap -lmbedtls -llwip -lnet80211 -lesp_http_server -lpthread -ljson -lesp_https_ota -lstdc++
|
||||
|
||||
compiler.as.cmd=xtensa-esp32-elf-as
|
||||
|
||||
|
@ -29,41 +29,36 @@ import struct
|
||||
import sys
|
||||
import hashlib
|
||||
import binascii
|
||||
import errno
|
||||
|
||||
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
|
||||
MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
|
||||
MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
|
||||
PARTITION_TABLE_SIZE = 0x1000 # Size of partition table
|
||||
|
||||
MIN_PARTITION_SUBTYPE_APP_OTA = 0x10
|
||||
NUM_PARTITION_SUBTYPE_APP_OTA = 16
|
||||
|
||||
__version__ = '1.2'
|
||||
|
||||
APP_TYPE = 0x00
|
||||
DATA_TYPE = 0x01
|
||||
|
||||
TYPES = {
|
||||
"app": APP_TYPE,
|
||||
"data": DATA_TYPE,
|
||||
"app" : APP_TYPE,
|
||||
"data" : DATA_TYPE,
|
||||
}
|
||||
|
||||
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
|
||||
SUBTYPES = {
|
||||
APP_TYPE: {
|
||||
"factory": 0x00,
|
||||
"test": 0x20,
|
||||
APP_TYPE : {
|
||||
"factory" : 0x00,
|
||||
"test" : 0x20,
|
||||
},
|
||||
DATA_TYPE: {
|
||||
"ota": 0x00,
|
||||
"phy": 0x01,
|
||||
"nvs": 0x02,
|
||||
"coredump": 0x03,
|
||||
"nvs_keys": 0x04,
|
||||
"efuse": 0x05,
|
||||
"esphttpd": 0x80,
|
||||
"fat": 0x81,
|
||||
"spiffs": 0x82,
|
||||
DATA_TYPE : {
|
||||
"ota" : 0x00,
|
||||
"phy" : 0x01,
|
||||
"nvs" : 0x02,
|
||||
"coredump" : 0x03,
|
||||
"nvs_keys" : 0x04,
|
||||
"esphttpd" : 0x80,
|
||||
"fat" : 0x81,
|
||||
"spiffs" : 0x82,
|
||||
},
|
||||
}
|
||||
|
||||
@ -72,19 +67,16 @@ md5sum = True
|
||||
secure = False
|
||||
offset_part_table = 0
|
||||
|
||||
|
||||
def status(msg):
|
||||
""" Print status message to stderr """
|
||||
if not quiet:
|
||||
critical(msg)
|
||||
|
||||
|
||||
def critical(msg):
|
||||
""" Print critical message to stderr """
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
class PartitionTable(list):
|
||||
def __init__(self):
|
||||
super(PartitionTable, self).__init__(self)
|
||||
@ -106,15 +98,15 @@ class PartitionTable(list):
|
||||
if line.startswith("#") or len(line) == 0:
|
||||
continue
|
||||
try:
|
||||
res.append(PartitionDefinition.from_csv(line, line_no + 1))
|
||||
res.append(PartitionDefinition.from_csv(line, line_no+1))
|
||||
except InputError as e:
|
||||
raise InputError("Error at line %d: %s" % (line_no + 1, e))
|
||||
raise InputError("Error at line %d: %s" % (line_no+1, e))
|
||||
except Exception:
|
||||
critical("Unexpected error parsing CSV line %d: %s" % (line_no + 1, line))
|
||||
critical("Unexpected error parsing CSV line %d: %s" % (line_no+1, line))
|
||||
raise
|
||||
|
||||
# fix up missing offsets & negative sizes
|
||||
last_end = offset_part_table + PARTITION_TABLE_SIZE # first offset after partition table
|
||||
last_end = offset_part_table + PARTITION_TABLE_SIZE # first offset after partition table
|
||||
for e in res:
|
||||
if e.offset is not None and e.offset < last_end:
|
||||
if e == res[0]:
|
||||
@ -153,14 +145,14 @@ class PartitionTable(list):
|
||||
ptype = TYPES[ptype]
|
||||
except KeyError:
|
||||
try:
|
||||
ptype = int(ptype, 0)
|
||||
ptypes = int(ptype, 0)
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
subtype = SUBTYPES[int(ptype)][subtype]
|
||||
except KeyError:
|
||||
try:
|
||||
ptype = int(ptype, 0)
|
||||
ptypes = int(ptype, 0)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
@ -179,11 +171,11 @@ class PartitionTable(list):
|
||||
# verify each partition individually
|
||||
for p in self:
|
||||
p.verify()
|
||||
|
||||
|
||||
# check on duplicate name
|
||||
names = [p.name for p in self]
|
||||
duplicates = set(n for n in names if names.count(n) > 1)
|
||||
|
||||
names = [ p.name for p in self ]
|
||||
duplicates = set( n for n in names if names.count(n) > 1 )
|
||||
|
||||
# print sorted duplicate partitions by name
|
||||
if len(duplicates) != 0:
|
||||
print("A list of partitions that have the same name:")
|
||||
@ -191,14 +183,14 @@ class PartitionTable(list):
|
||||
if len(duplicates.intersection([p.name])) != 0:
|
||||
print("%s" % (p.to_csv()))
|
||||
raise InputError("Partition names must be unique")
|
||||
|
||||
|
||||
# check for overlaps
|
||||
last = None
|
||||
for p in sorted(self, key=lambda x:x.offset):
|
||||
if p.offset < offset_part_table + PARTITION_TABLE_SIZE:
|
||||
raise InputError("Partition offset 0x%x is below 0x%x" % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
|
||||
if last is not None and p.offset < last.offset + last.size:
|
||||
raise InputError("Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset, last.offset, last.offset + last.size - 1))
|
||||
raise InputError("Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset, last.offset, last.offset+last.size-1))
|
||||
last = p
|
||||
|
||||
def flash_size(self):
|
||||
@ -213,17 +205,17 @@ class PartitionTable(list):
|
||||
|
||||
@classmethod
|
||||
def from_binary(cls, b):
|
||||
md5 = hashlib.md5()
|
||||
md5 = hashlib.md5();
|
||||
result = cls()
|
||||
for o in range(0,len(b),32):
|
||||
data = b[o:o + 32]
|
||||
data = b[o:o+32]
|
||||
if len(data) != 32:
|
||||
raise InputError("Partition table length must be a multiple of 32 bytes")
|
||||
if data == b'\xFF' * 32:
|
||||
if data == b'\xFF'*32:
|
||||
return result # got end marker
|
||||
if md5sum and data[:2] == MD5_PARTITION_BEGIN[:2]: # check only the magic number part
|
||||
if md5sum and data[:2] == MD5_PARTITION_BEGIN[:2]: #check only the magic number part
|
||||
if data[16:] == md5.digest():
|
||||
continue # the next iteration will check for the end marker
|
||||
continue # the next iteration will check for the end marker
|
||||
else:
|
||||
raise InputError("MD5 checksums don't match! (computed: 0x%s, parsed: 0x%s)" % (md5.hexdigest(), binascii.hexlify(data[16:])))
|
||||
else:
|
||||
@ -235,35 +227,34 @@ class PartitionTable(list):
|
||||
result = b"".join(e.to_binary() for e in self)
|
||||
if md5sum:
|
||||
result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
|
||||
if len(result) >= MAX_PARTITION_LENGTH:
|
||||
if len(result )>= MAX_PARTITION_LENGTH:
|
||||
raise InputError("Binary partition table length (%d) longer than max" % len(result))
|
||||
result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
|
||||
return result
|
||||
|
||||
def to_csv(self, simple_formatting=False):
|
||||
rows = ["# Espressif ESP32 Partition Table",
|
||||
"# Name, Type, SubType, Offset, Size, Flags"]
|
||||
rows += [x.to_csv(simple_formatting) for x in self]
|
||||
rows = [ "# Espressif ESP32 Partition Table",
|
||||
"# Name, Type, SubType, Offset, Size, Flags" ]
|
||||
rows += [ x.to_csv(simple_formatting) for x in self ]
|
||||
return "\n".join(rows) + "\n"
|
||||
|
||||
|
||||
class PartitionDefinition(object):
|
||||
MAGIC_BYTES = b"\xAA\x50"
|
||||
|
||||
ALIGNMENT = {
|
||||
APP_TYPE: 0x10000,
|
||||
DATA_TYPE: 0x04,
|
||||
APP_TYPE : 0x10000,
|
||||
DATA_TYPE : 0x04,
|
||||
}
|
||||
|
||||
# dictionary maps flag name (as used in CSV flags list, property name)
|
||||
# to bit set in flags words in binary format
|
||||
FLAGS = {
|
||||
"encrypted": 0
|
||||
"encrypted" : 0
|
||||
}
|
||||
|
||||
# add subtypes for the 16 OTA slot values ("ota_XX, etc.")
|
||||
for ota_slot in range(NUM_PARTITION_SUBTYPE_APP_OTA):
|
||||
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = MIN_PARTITION_SUBTYPE_APP_OTA + ota_slot
|
||||
for ota_slot in range(16):
|
||||
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot
|
||||
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
@ -277,7 +268,7 @@ class PartitionDefinition(object):
|
||||
def from_csv(cls, line, line_no):
|
||||
""" Parse a line from the CSV """
|
||||
line_w_defaults = line + ",,,," # lazy way to support default fields
|
||||
fields = [f.strip() for f in line_w_defaults.split(",")]
|
||||
fields = [ f.strip() for f in line_w_defaults.split(",") ]
|
||||
|
||||
res = PartitionDefinition()
|
||||
res.line_no = line_no
|
||||
@ -307,7 +298,7 @@ class PartitionDefinition(object):
|
||||
def maybe_hex(x):
|
||||
return "0x%x" % x if x is not None else "None"
|
||||
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type, self.subtype or 0,
|
||||
maybe_hex(self.offset), maybe_hex(self.size))
|
||||
maybe_hex(self.offset), maybe_hex(self.size))
|
||||
|
||||
def __str__(self):
|
||||
return "Part '%s' %d/%d @ 0x%x size 0x%x" % (self.name, self.type, self.subtype, self.offset or -1, self.size or -1)
|
||||
@ -334,7 +325,7 @@ class PartitionDefinition(object):
|
||||
|
||||
def parse_subtype(self, strval):
|
||||
if strval == "":
|
||||
return 0 # default
|
||||
return 0 # default
|
||||
return parse_int(strval, SUBTYPES.get(self.type, {}))
|
||||
|
||||
def parse_address(self, strval):
|
||||
@ -358,14 +349,12 @@ class PartitionDefinition(object):
|
||||
raise ValidationError(self, "Size field is not set")
|
||||
|
||||
if self.name in TYPES and TYPES.get(self.name, "") != self.type:
|
||||
critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's "
|
||||
"type (0x%x). Mistake in partition table?" % (self.name, self.type))
|
||||
critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's type (0x%x). Mistake in partition table?" % (self.name, self.type))
|
||||
all_subtype_names = []
|
||||
for names in (t.keys() for t in SUBTYPES.values()):
|
||||
all_subtype_names += names
|
||||
if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, "") != self.subtype:
|
||||
critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has "
|
||||
"non-matching type 0x%x and subtype 0x%x. Mistake in partition table?" % (self.name, self.type, self.subtype))
|
||||
critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has non-matching type 0x%x and subtype 0x%x. Mistake in partition table?" % (self.name, self.type, self.subtype))
|
||||
|
||||
STRUCT_FORMAT = b"<2sBBLL16sL"
|
||||
|
||||
@ -376,21 +365,21 @@ class PartitionDefinition(object):
|
||||
res = cls()
|
||||
(magic, res.type, res.subtype, res.offset,
|
||||
res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
|
||||
if b"\x00" in res.name: # strip null byte padding from name string
|
||||
if b"\x00" in res.name: # strip null byte padding from name string
|
||||
res.name = res.name[:res.name.index(b"\x00")]
|
||||
res.name = res.name.decode()
|
||||
if magic != cls.MAGIC_BYTES:
|
||||
raise InputError("Invalid magic bytes (%r) for partition definition" % magic)
|
||||
for flag,bit in cls.FLAGS.items():
|
||||
if flags & (1 << bit):
|
||||
if flags & (1<<bit):
|
||||
setattr(res, flag, True)
|
||||
flags &= ~(1 << bit)
|
||||
flags &= ~(1<<bit)
|
||||
if flags != 0:
|
||||
critical("WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?" % flags)
|
||||
return res
|
||||
|
||||
def get_flags_list(self):
|
||||
return [flag for flag in self.FLAGS.keys() if getattr(self, flag)]
|
||||
return [ flag for flag in self.FLAGS.keys() if getattr(self, flag) ]
|
||||
|
||||
def to_binary(self):
|
||||
flags = sum((1 << self.FLAGS[flag]) for flag in self.get_flags_list())
|
||||
@ -404,14 +393,14 @@ class PartitionDefinition(object):
|
||||
def to_csv(self, simple_formatting=False):
|
||||
def addr_format(a, include_sizes):
|
||||
if not simple_formatting and include_sizes:
|
||||
for (val, suffix) in [(0x100000, "M"), (0x400, "K")]:
|
||||
for (val, suffix) in [ (0x100000, "M"), (0x400, "K") ]:
|
||||
if a % val == 0:
|
||||
return "%d%s" % (a // val, suffix)
|
||||
return "0x%x" % a
|
||||
|
||||
def lookup_keyword(t, keywords):
|
||||
for k,v in keywords.items():
|
||||
if simple_formatting is False and t == v:
|
||||
if simple_formatting == False and t == v:
|
||||
return k
|
||||
return "%d" % t
|
||||
|
||||
@ -419,12 +408,12 @@ class PartitionDefinition(object):
|
||||
""" colon-delimited list of flags """
|
||||
return ":".join(self.get_flags_list())
|
||||
|
||||
return ",".join([self.name,
|
||||
lookup_keyword(self.type, TYPES),
|
||||
lookup_keyword(self.subtype, SUBTYPES.get(self.type, {})),
|
||||
addr_format(self.offset, False),
|
||||
addr_format(self.size, True),
|
||||
generate_text_flags()])
|
||||
return ",".join([ self.name,
|
||||
lookup_keyword(self.type, TYPES),
|
||||
lookup_keyword(self.subtype, SUBTYPES.get(self.type, {})),
|
||||
addr_format(self.offset, False),
|
||||
addr_format(self.size, True),
|
||||
generate_text_flags()])
|
||||
|
||||
|
||||
def parse_int(v, keywords={}):
|
||||
@ -432,7 +421,7 @@ def parse_int(v, keywords={}):
|
||||
k/m/K/M suffixes and 'keyword' value lookup.
|
||||
"""
|
||||
try:
|
||||
for letter, multiplier in [("k", 1024), ("m", 1024 * 1024)]:
|
||||
for letter, multiplier in [ ("k",1024), ("m",1024*1024) ]:
|
||||
if v.lower().endswith(letter):
|
||||
return parse_int(v[:-1], keywords) * multiplier
|
||||
return int(v, 0)
|
||||
@ -444,7 +433,6 @@ def parse_int(v, keywords={}):
|
||||
except KeyError:
|
||||
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
|
||||
|
||||
|
||||
def main():
|
||||
global quiet
|
||||
global md5sum
|
||||
@ -453,11 +441,10 @@ def main():
|
||||
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
|
||||
|
||||
parser.add_argument('--flash-size', help='Optional flash size limit, checks partition table fits in flash',
|
||||
nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB'])
|
||||
nargs='?', choices=[ '1MB', '2MB', '4MB', '8MB', '16MB' ])
|
||||
parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true')
|
||||
parser.add_argument('--no-verify', help="Don't verify partition table fields", action='store_true')
|
||||
parser.add_argument('--verify', '-v', help="Verify partition table fields (deprecated, this behaviour is "
|
||||
"enabled by default and this flag does nothing.", action='store_true')
|
||||
parser.add_argument('--verify', '-v', help="Verify partition table fields (deprecated, this behaviour is enabled by default and this flag does nothing.", action='store_true')
|
||||
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
|
||||
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
|
||||
parser.add_argument('--secure', help="Require app partitions to be suitable for secure boot", action='store_true')
|
||||
@ -490,20 +477,9 @@ def main():
|
||||
size = size_mb * 1024 * 1024 # flash memory uses honest megabytes!
|
||||
table_size = table.flash_size()
|
||||
if size < table_size:
|
||||
raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which does not fit in configured "
|
||||
"flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu." %
|
||||
raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which does not fit in configured flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu." %
|
||||
(args.input.name, table_size / 1024.0 / 1024.0, table_size, size_mb))
|
||||
|
||||
# Make sure that the output directory is created
|
||||
output_dir = os.path.abspath(os.path.dirname(args.output))
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
try:
|
||||
os.makedirs(output_dir)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
if input_is_binary:
|
||||
output = table.to_csv()
|
||||
with sys.stdout if args.output == '-' else open(args.output, 'w') as f:
|
||||
|
@ -106,7 +106,6 @@ env.Append(
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "coap"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "console"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "driver"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "efuse"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp-tls"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp32"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_adc_cal"),
|
||||
@ -114,9 +113,7 @@ env.Append(
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_http_client"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_http_server"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_https_ota"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_https_server"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "esp_ringbuf"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "espcoredump"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "ethernet"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "expat"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "fatfs"),
|
||||
@ -148,7 +145,6 @@ env.Append(
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "tcp_transport"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "tcpip_adapter"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "ulp"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "unity"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "vfs"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "wear_levelling"),
|
||||
join(FRAMEWORK_DIR, "tools", "sdk", "include", "wifi_provisioning"),
|
||||
@ -166,7 +162,7 @@ env.Append(
|
||||
],
|
||||
|
||||
LIBS=[
|
||||
"-lgcc", "-lopenssl", "-lbtdm_app", "-lfatfs", "-lwps", "-lcoexist", "-lwear_levelling", "-lesp_http_client", "-lprotobuf-c", "-lhal", "-lnewlib", "-ldriver", "-lbootloader_support", "-lpp", "-lfreemodbus", "-lmesh", "-lsmartconfig", "-ljsmn", "-lwpa", "-lethernet", "-lphy", "-lfrmn", "-lapp_trace", "-lfr_coefficients", "-lconsole", "-lulp", "-lwpa_supplicant", "-lfreertos", "-lbt", "-lmicro-ecc", "-lesp32-camera", "-lcxx", "-lxtensa-debug-module", "-ltcp_transport", "-lmdns", "-lvfs", "-lmtmn", "-lespcoredump", "-lesp_ringbuf", "-lsoc", "-lcore", "-lfb_gfx", "-lsdmmc", "-llibsodium", "-lcoap", "-ltcpip_adapter", "-lprotocomm", "-lesp_event", "-limage_util", "-lc_nano", "-lesp-tls", "-lasio", "-lrtc", "-lspi_flash", "-lwpa2", "-lwifi_provisioning", "-lesp32", "-lface_recognition", "-lapp_update", "-lnghttp", "-lspiffs", "-lface_detection", "-lefuse", "-lunity", "-lesp_https_server", "-lespnow", "-lnvs_flash", "-lesp_adc_cal", "-llog", "-ldl_lib", "-lsmartconfig_ack", "-lexpat", "-lfd_coefficients", "-lm", "-lmqtt", "-lc", "-lheap", "-lmbedtls", "-llwip", "-lnet80211", "-lesp_http_server", "-lpthread", "-ljson", "-lesp_https_ota", "-lstdc++"
|
||||
"-lgcc", "-lopenssl", "-lbtdm_app", "-lfatfs", "-lwps", "-lcoexist", "-lwear_levelling", "-lesp_http_client", "-lprotobuf-c", "-lhal", "-lnewlib", "-ldriver", "-lbootloader_support", "-lpp", "-lfreemodbus", "-lmesh", "-lsmartconfig", "-ljsmn", "-lwpa", "-lethernet", "-lphy", "-lfrmn", "-lapp_trace", "-lfr_coefficients", "-lconsole", "-lulp", "-lwpa_supplicant", "-lfreertos", "-lbt", "-lmicro-ecc", "-lesp32-camera", "-lcxx", "-lxtensa-debug-module", "-ltcp_transport", "-lmdns", "-lvfs", "-lmtmn", "-lesp_ringbuf", "-lsoc", "-lcore", "-lfb_gfx", "-lsdmmc", "-llibsodium", "-lcoap", "-ltcpip_adapter", "-lprotocomm", "-lesp_event", "-limage_util", "-lc_nano", "-lesp-tls", "-lasio", "-lrtc", "-lspi_flash", "-lwpa2", "-lwifi_provisioning", "-lesp32", "-lface_recognition", "-lapp_update", "-lnghttp", "-lspiffs", "-lface_detection", "-lespnow", "-lnvs_flash", "-lesp_adc_cal", "-llog", "-ldl_lib", "-lsmartconfig_ack", "-lexpat", "-lfd_coefficients", "-lm", "-lmqtt", "-lc", "-lheap", "-lmbedtls", "-llwip", "-lnet80211", "-lesp_http_server", "-lpthread", "-ljson", "-lesp_https_ota", "-lstdc++"
|
||||
],
|
||||
|
||||
LIBSOURCE_DIRS=[
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -51,7 +51,7 @@ void esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size);
|
||||
*
|
||||
* @param dest Indicates HW interface to send data.
|
||||
* @param size Size of data to write to trace buffer.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return non-NULL on success, otherwise NULL.
|
||||
*/
|
||||
@ -63,7 +63,7 @@ uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32
|
||||
*
|
||||
* @param dest Indicates HW interface to send data. Should be identical to the same parameter in call to esp_apptrace_buffer_get.
|
||||
* @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_buffer_get.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
@ -75,7 +75,7 @@ esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32
|
||||
* @param dest Indicates HW interface to send data.
|
||||
* @param data Address of data to write to trace buffer.
|
||||
* @param size Size of data to write to trace buffer.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
@ -85,7 +85,7 @@ esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_
|
||||
* @brief vprintf-like function to sent log messages to host via specified HW interface.
|
||||
*
|
||||
* @param dest Indicates HW interface to send data.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
* @param fmt Address of format string.
|
||||
* @param ap List of arguments.
|
||||
*
|
||||
@ -107,7 +107,7 @@ int esp_apptrace_vprintf(const char *fmt, va_list ap);
|
||||
* @brief Flushes remaining data in trace buffer to host.
|
||||
*
|
||||
* @param dest Indicates HW interface to flush data on.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
@ -119,7 +119,7 @@ esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t tmo);
|
||||
*
|
||||
* @param dest Indicates HW interface to flush data on.
|
||||
* @param min_sz Threshold for flushing data. If current filling level is above this value, data will be flushed. TRAX destinations only.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
@ -131,31 +131,31 @@ esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, u
|
||||
* @param dest Indicates HW interface to read the data on.
|
||||
* @param data Address of buffer to put data from trace buffer.
|
||||
* @param size Pointer to store size of read data. Before call to this function pointed memory must hold requested size of data
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *data, uint32_t *size, uint32_t tmo);
|
||||
|
||||
/**
|
||||
* @brief Retrieves incoming data buffer if any.
|
||||
* @brief Rertrieves incoming data buffer if any.
|
||||
* After data in buffer are processed esp_apptrace_down_buffer_put must be called to indicate it.
|
||||
*
|
||||
* @param dest Indicates HW interface to receive data.
|
||||
* @param size Address to store size of available data in down buffer. Must be initialized with requested value.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param size Address to store size of available data in down buffer. Must be initializaed with requested value.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return non-NULL on success, otherwise NULL.
|
||||
*/
|
||||
uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size, uint32_t tmo);
|
||||
|
||||
/**
|
||||
* @brief Indicates that the data in down buffer are processed.
|
||||
* @brief Indicates that the data in down buffer are processesd.
|
||||
* This function is a counterpart of and must be preceeded by esp_apptrace_down_buffer_get.
|
||||
*
|
||||
* @param dest Indicates HW interface to receive data. Should be identical to the same parameter in call to esp_apptrace_down_buffer_get.
|
||||
* @param ptr Address of trace buffer to release. Should be the value returned by call to esp_apptrace_down_buffer_get.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinitely.
|
||||
* @param tmo Timeout for operation (in us). Use ESP_APPTRACE_TMO_INFINITE to wait indefinetly.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise see esp_err_t
|
||||
*/
|
||||
@ -247,7 +247,7 @@ int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream);
|
||||
|
||||
/**
|
||||
* @brief Indicates to the host that all file operations are completed.
|
||||
* This function should be called after all file operations are finished and
|
||||
* This function should be called after all file operations are finished and
|
||||
* indicate to the host that it can perform cleanup operations (close open files etc.).
|
||||
*
|
||||
* @param dest Indicates HW interface to use.
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_flash_data_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -34,10 +32,6 @@ extern "C"
|
||||
#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< Error if request was to write or erase the current running partition */
|
||||
#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< Error if OTA data partition contains invalid content */
|
||||
#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< Error if OTA app image is invalid */
|
||||
#define ESP_ERR_OTA_SMALL_SEC_VER (ESP_ERR_OTA_BASE + 0x04) /*!< Error if the firmware has a secure version less than the running firmware. */
|
||||
#define ESP_ERR_OTA_ROLLBACK_FAILED (ESP_ERR_OTA_BASE + 0x05) /*!< Error if flash does not have valid firmware in passive partition and hence rollback is not possible */
|
||||
#define ESP_ERR_OTA_ROLLBACK_INVALID_STATE (ESP_ERR_OTA_BASE + 0x06) /*!< Error if current active firmware is still marked in pending validation state (ESP_OTA_IMG_PENDING_VERIFY), essentially first boot of firmware image post upgrade and hence firmware upgrade is not possible */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Opaque handle for an application OTA update
|
||||
@ -47,24 +41,6 @@ extern "C"
|
||||
*/
|
||||
typedef uint32_t esp_ota_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Return esp_app_desc structure. This structure includes app version.
|
||||
*
|
||||
* Return description for running app.
|
||||
* @return Pointer to esp_app_desc structure.
|
||||
*/
|
||||
const esp_app_desc_t *esp_ota_get_app_description(void);
|
||||
|
||||
/**
|
||||
* @brief Fill the provided buffer with SHA256 of the ELF file, formatted as hexadecimal, null-terminated.
|
||||
* If the buffer size is not sufficient to fit the entire SHA256 in hex plus a null terminator,
|
||||
* the largest possible number of bytes will be written followed by a null.
|
||||
* @param dst Destination buffer
|
||||
* @param size Size of the buffer
|
||||
* @return Number of bytes written to dst (including null terminator)
|
||||
*/
|
||||
int esp_ota_get_app_elf_sha256(char* dst, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Commence an OTA update writing to the specified partition.
|
||||
|
||||
@ -76,10 +52,6 @@ int esp_ota_get_app_elf_sha256(char* dst, size_t size);
|
||||
* On success, this function allocates memory that remains in use
|
||||
* until esp_ota_end() is called with the returned handle.
|
||||
*
|
||||
* Note: If the rollback option is enabled and the running application has the ESP_OTA_IMG_PENDING_VERIFY state then
|
||||
* it will lead to the ESP_ERR_OTA_ROLLBACK_INVALID_STATE error. Confirm the running app before to run download a new app,
|
||||
* use esp_ota_mark_app_valid_cancel_rollback() function for it (this should be done as early as possible when you first download a new application).
|
||||
*
|
||||
* @param partition Pointer to info for partition which will receive the OTA update. Required.
|
||||
* @param image_size Size of new OTA app image. Partition will be erased in order to receive this size of image. If 0 or OTA_SIZE_UNKNOWN, the entire partition is erased.
|
||||
* @param out_handle On success, returns a handle which should be used for subsequent esp_ota_write() and esp_ota_end() calls.
|
||||
@ -93,7 +65,6 @@ int esp_ota_get_app_elf_sha256(char* dst, size_t size);
|
||||
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
|
||||
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
|
||||
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
|
||||
* - ESP_ERR_OTA_ROLLBACK_INVALID_STATE: If the running app has not confirmed state. Before performing an update, the application must be valid.
|
||||
*/
|
||||
esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle);
|
||||
|
||||
@ -199,83 +170,6 @@ const esp_partition_t* esp_ota_get_running_partition(void);
|
||||
*/
|
||||
const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from);
|
||||
|
||||
/**
|
||||
* @brief Returns esp_app_desc structure for app partition. This structure includes app version.
|
||||
*
|
||||
* Returns a description for the requested app partition.
|
||||
* @param[in] partition Pointer to app partition. (only app partition)
|
||||
* @param[out] app_desc Structure of info about app.
|
||||
* @return
|
||||
* - ESP_OK Successful.
|
||||
* - ESP_ERR_NOT_FOUND app_desc structure is not found. Magic word is incorrect.
|
||||
* - ESP_ERR_NOT_SUPPORTED Partition is not application.
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL or if partition's offset exceeds partition size.
|
||||
* - ESP_ERR_INVALID_SIZE Read would go out of bounds of the partition.
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc);
|
||||
|
||||
/**
|
||||
* @brief This function is called to indicate that the running app is working well.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: if successful.
|
||||
*/
|
||||
esp_err_t esp_ota_mark_app_valid_cancel_rollback();
|
||||
|
||||
/**
|
||||
* @brief This function is called to roll back to the previously workable app with reboot.
|
||||
*
|
||||
* If rollback is successful then device will reset else API will return with error code.
|
||||
* Checks applications on a flash drive that can be booted in case of rollback.
|
||||
* If the flash does not have at least one app (except the running app) then rollback is not possible.
|
||||
* @return
|
||||
* - ESP_FAIL: if not successful.
|
||||
* - ESP_ERR_OTA_ROLLBACK_FAILED: The rollback is not possible due to flash does not have any apps.
|
||||
*/
|
||||
esp_err_t esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
|
||||
/**
|
||||
* @brief Returns last partition with invalid state (ESP_OTA_IMG_INVALID or ESP_OTA_IMG_ABORTED).
|
||||
*
|
||||
* @return partition.
|
||||
*/
|
||||
const esp_partition_t* esp_ota_get_last_invalid_partition();
|
||||
|
||||
/**
|
||||
* @brief Returns state for given partition.
|
||||
*
|
||||
* @param[in] partition Pointer to partition.
|
||||
* @param[out] ota_state state of partition (if this partition has a record in otadata).
|
||||
* @return
|
||||
* - ESP_OK: Successful.
|
||||
* - ESP_ERR_INVALID_ARG: partition or ota_state arguments were NULL.
|
||||
* - ESP_ERR_NOT_SUPPORTED: partition is not ota.
|
||||
* - ESP_ERR_NOT_FOUND: Partition table does not have otadata or state was not found for given partition.
|
||||
*/
|
||||
esp_err_t esp_ota_get_state_partition(const esp_partition_t *partition, esp_ota_img_states_t *ota_state);
|
||||
|
||||
/**
|
||||
* @brief Erase previous boot app partition and corresponding otadata select for this partition.
|
||||
*
|
||||
* When current app is marked to as valid then you can erase previous app partition.
|
||||
* @return
|
||||
* - ESP_OK: Successful, otherwise ESP_ERR.
|
||||
*/
|
||||
esp_err_t esp_ota_erase_last_boot_app_partition(void);
|
||||
|
||||
/**
|
||||
* @brief Checks applications on the slots which can be booted in case of rollback.
|
||||
*
|
||||
* These applications should be valid (marked in otadata as not UNDEFINED, INVALID or ABORTED and crc is good) and be able booted,
|
||||
* and secure_version of app >= secure_version of efuse (if anti-rollback is enabled).
|
||||
*
|
||||
* @return
|
||||
* - True: Returns true if the slots have at least one app (except the running app).
|
||||
* - False: The rollback is not possible.
|
||||
*/
|
||||
bool esp_ota_check_rollback_is_possible(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
#pragma once
|
||||
#include "esp_flash_data_types.h"
|
||||
#include "esp_image_format.h"
|
||||
|
||||
/// Type of hold a GPIO in low state
|
||||
typedef enum {
|
||||
@ -24,29 +23,21 @@ typedef enum {
|
||||
} esp_comm_gpio_hold_t;
|
||||
|
||||
/**
|
||||
* @brief Calculate crc for the OTA data select.
|
||||
* @brief Calculate crc for the OTA data partition.
|
||||
*
|
||||
* @param[in] s The OTA data select.
|
||||
* @param[in] ota_data The OTA data partition.
|
||||
* @return Returns crc value.
|
||||
*/
|
||||
uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s);
|
||||
|
||||
/**
|
||||
* @brief Verifies the validity of the OTA data select
|
||||
* @brief Verifies the validity of the OTA data partition
|
||||
*
|
||||
* @param[in] s The OTA data select.
|
||||
* @param[in] ota_data The OTA data partition.
|
||||
* @return Returns true on valid, false otherwise.
|
||||
*/
|
||||
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s);
|
||||
|
||||
/**
|
||||
* @brief Returns true if OTADATA is not marked as bootable partition.
|
||||
*
|
||||
* @param[in] s The OTA data select.
|
||||
* @return Returns true if OTADATA invalid, false otherwise.
|
||||
*/
|
||||
bool bootloader_common_ota_select_invalid(const esp_ota_select_entry_t *s);
|
||||
|
||||
/**
|
||||
* @brief Check if the GPIO input is a long hold or a short hold.
|
||||
*
|
||||
@ -100,39 +91,3 @@ bool bootloader_common_label_search(const char *list, char *label);
|
||||
* - ESP_FAIL: An allocation error occurred.
|
||||
*/
|
||||
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of active otadata.
|
||||
*
|
||||
* @param[in] two_otadata Pointer on array from two otadata structures.
|
||||
*
|
||||
* @return The number of active otadata (0 or 1).
|
||||
* - -1: If it does not have active otadata.
|
||||
*/
|
||||
int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of active otadata.
|
||||
*
|
||||
* @param[in] two_otadata Pointer on array from two otadata structures.
|
||||
* @param[in] valid_two_otadata Pointer on array from two bools. True means select.
|
||||
* @param[in] max True - will select the maximum ota_seq number, otherwise the minimum.
|
||||
*
|
||||
* @return The number of active otadata (0 or 1).
|
||||
* - -1: If it does not have active otadata.
|
||||
*/
|
||||
int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata, bool *valid_two_otadata, bool max);
|
||||
|
||||
/**
|
||||
* @brief Returns esp_app_desc structure for app partition. This structure includes app version.
|
||||
*
|
||||
* Returns a description for the requested app partition.
|
||||
* @param[in] partition App partition description.
|
||||
* @param[out] app_desc Structure of info about app.
|
||||
* @return
|
||||
* - ESP_OK: Successful.
|
||||
* - ESP_ERR_INVALID_ARG: The arguments passed are not valid.
|
||||
* - ESP_ERR_NOT_FOUND: app_desc structure is not found. Magic word is incorrect.
|
||||
* - ESP_FAIL: mapping is fail.
|
||||
*/
|
||||
esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc);
|
||||
|
99
tools/sdk/include/bootloader_support/esp_efuse.h
Normal file
99
tools/sdk/include/bootloader_support/esp_efuse.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _ESP_EFUSE_H
|
||||
#define _ESP_EFUSE_H
|
||||
|
||||
#include "soc/efuse_reg.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* @brief Permanently update values written to the efuse write registers
|
||||
*
|
||||
* After updating EFUSE_BLKx_WDATAx_REG registers with new values to
|
||||
* write, call this function to permanently write them to efuse.
|
||||
*
|
||||
* @note Setting bits in efuse is permanent, they cannot be unset.
|
||||
*
|
||||
* @note Due to this restriction you don't need to copy values to
|
||||
* Efuse write registers from the matching read registers, bits which
|
||||
* are set in the read register but unset in the matching write
|
||||
* register will be unchanged when new values are burned.
|
||||
*
|
||||
* @note This function is not threadsafe, if calling code updates
|
||||
* efuse values from multiple tasks then this is caller's
|
||||
* responsibility to serialise.
|
||||
*
|
||||
* After burning new efuses, the read registers are updated to match
|
||||
* the new efuse values.
|
||||
*/
|
||||
void esp_efuse_burn_new_values(void);
|
||||
|
||||
/* @brief Reset efuse write registers
|
||||
*
|
||||
* Efuse write registers are written to zero, to negate
|
||||
* any changes that have been staged here.
|
||||
*/
|
||||
void esp_efuse_reset(void);
|
||||
|
||||
/* @brief Disable BASIC ROM Console via efuse
|
||||
*
|
||||
* By default, if booting from flash fails the ESP32 will boot a
|
||||
* BASIC console in ROM.
|
||||
*
|
||||
* Call this function (from bootloader or app) to permanently
|
||||
* disable the console on this chip.
|
||||
*/
|
||||
void esp_efuse_disable_basic_rom_console(void);
|
||||
|
||||
/* @brief Encode one or more sets of 6 byte sequences into
|
||||
* 8 bytes suitable for 3/4 Coding Scheme.
|
||||
*
|
||||
* This function is only useful if the CODING_SCHEME efuse
|
||||
* is set to value 1 for 3/4 Coding Scheme.
|
||||
*
|
||||
* @param[in] in_bytes Pointer to a sequence of bytes to encode for 3/4 Coding Scheme. Must have length in_bytes_len. After being written to hardware, these bytes will read back as little-endian words.
|
||||
* @param[out] out_words Pointer to array of words suitable for writing to efuse write registers. Array must contain 2 words (8 bytes) for every 6 bytes in in_bytes_len. Can be a pointer to efuse write registers.
|
||||
* @param in_bytes_len. Length of array pointed to by in_bytes, in bytes. Must be a multiple of 6.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if either pointer is null or in_bytes_len is not a multiple of 6. ESP_OK otherwise.
|
||||
*/
|
||||
esp_err_t esp_efuse_apply_34_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len);
|
||||
|
||||
/* @brief Write random data to efuse key block write registers
|
||||
*
|
||||
* @note Caller is responsible for ensuring efuse
|
||||
* block is empty and not write protected, before calling.
|
||||
*
|
||||
* @note Behaviour depends on coding scheme: a 256-bit key is
|
||||
* generated and written for Coding Scheme "None", a 192-bit key
|
||||
* is generated, extended to 256-bits by the Coding Scheme,
|
||||
* and then writtten for 3/4 Coding Scheme.
|
||||
*
|
||||
* @note This function does not burn the new values, caller should
|
||||
* call esp_efuse_burn_new_values() when ready to do this.
|
||||
*
|
||||
* @param blk_wdata0_reg Address of the first data write register
|
||||
* in the block
|
||||
*/
|
||||
void esp_efuse_write_random_key(uint32_t blk_wdata0_reg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_EFUSE_H */
|
||||
|
@ -89,25 +89,6 @@ typedef struct {
|
||||
uint32_t data_len;
|
||||
} esp_image_segment_header_t;
|
||||
|
||||
#define ESP_APP_DESC_MAGIC_WORD 0xABCD5432 /*!< The magic word for the esp_app_desc structure that is in DROM. */
|
||||
|
||||
/**
|
||||
* @brief Description about application.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t magic_word; /*!< Magic word ESP_APP_DESC_MAGIC_WORD */
|
||||
uint32_t secure_version; /*!< Secure version */
|
||||
uint32_t reserv1[2]; /*!< --- */
|
||||
char version[32]; /*!< Application version */
|
||||
char project_name[32]; /*!< Project name */
|
||||
char time[16]; /*!< Compile time */
|
||||
char date[16]; /*!< Compile date*/
|
||||
char idf_ver[32]; /*!< Version IDF */
|
||||
uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */
|
||||
uint32_t reserv2[20]; /*!< --- */
|
||||
} esp_app_desc_t;
|
||||
_Static_assert(sizeof(esp_app_desc_t) == 256, "esp_app_desc_t should be 256 bytes");
|
||||
|
||||
#define ESP_IMAGE_MAX_SEGMENTS 16
|
||||
|
||||
/* Structure to hold on-flash image metadata */
|
||||
|
@ -54,7 +54,6 @@ typedef uint8_t esp_ble_key_type_t;
|
||||
#define ESP_LE_AUTH_NO_BOND 0x00 /*!< 0*/ /* relate to BTM_LE_AUTH_NO_BOND in stack/btm_api.h */
|
||||
#define ESP_LE_AUTH_BOND 0x01 /*!< 1 << 0 */ /* relate to BTM_LE_AUTH_BOND in stack/btm_api.h */
|
||||
#define ESP_LE_AUTH_REQ_MITM (1 << 2) /*!< 1 << 2 */ /* relate to BTM_LE_AUTH_REQ_MITM in stack/btm_api.h */
|
||||
#define ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)/*!< 0101*/
|
||||
#define ESP_LE_AUTH_REQ_SC_ONLY (1 << 3) /*!< 1 << 3 */ /* relate to BTM_LE_AUTH_REQ_SC_ONLY in stack/btm_api.h */
|
||||
#define ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1001 */ /* relate to BTM_LE_AUTH_REQ_SC_BOND in stack/btm_api.h */
|
||||
#define ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1100 */ /* relate to BTM_LE_AUTH_REQ_SC_MITM in stack/btm_api.h */
|
||||
@ -64,9 +63,6 @@ typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit
|
||||
#define ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE 0
|
||||
#define ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE 1
|
||||
|
||||
#define ESP_BLE_OOB_DISABLE 0
|
||||
#define ESP_BLE_OOB_ENABLE 1
|
||||
|
||||
/* relate to BTM_IO_CAP_xxx in stack/btm_api.h */
|
||||
#define ESP_IO_CAP_OUT 0 /*!< DisplayOnly */ /* relate to BTM_IO_CAP_OUT in stack/btm_api.h */
|
||||
#define ESP_IO_CAP_IO 1 /*!< DisplayYesNo */ /* relate to BTM_IO_CAP_IO in stack/btm_api.h */
|
||||
@ -275,7 +271,6 @@ typedef enum {
|
||||
ESP_BLE_SM_SET_STATIC_PASSKEY,
|
||||
ESP_BLE_SM_CLEAR_STATIC_PASSKEY,
|
||||
ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH,
|
||||
ESP_BLE_SM_OOB_SUPPORT,
|
||||
ESP_BLE_SM_MAX_PARAM,
|
||||
} esp_ble_sm_param_t;
|
||||
|
||||
@ -558,6 +553,7 @@ typedef enum {
|
||||
ESP_GAP_SEARCH_DISC_CMPL_EVT = 4, /*!< Discovery complete. */
|
||||
ESP_GAP_SEARCH_DI_DISC_CMPL_EVT = 5, /*!< Discovery complete. */
|
||||
ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT = 6, /*!< Search cancelled */
|
||||
ESP_GAP_SEARCH_INQ_DISCARD_NUM_EVT = 7, /*!< The number of pkt discarded by flow control */
|
||||
} esp_gap_search_evt_t;
|
||||
|
||||
/**
|
||||
@ -635,6 +631,7 @@ typedef union {
|
||||
int num_resps; /*!< Scan result number */
|
||||
uint8_t adv_data_len; /*!< Adv data length */
|
||||
uint8_t scan_rsp_len; /*!< Scan response length */
|
||||
uint32_t num_dis; /*!< The number of discard packets */
|
||||
} scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */
|
||||
/**
|
||||
* @brief ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT
|
||||
@ -1140,7 +1137,7 @@ esp_err_t esp_ble_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t pas
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reply the confirm value to the peer device in the secure connection stage.
|
||||
* @brief Reply the confirm value to the peer device in the legacy connection stage.
|
||||
*
|
||||
* @param[in] bd_addr : BD address of the peer device
|
||||
* @param[in] accept : numbers to compare are the same or different.
|
||||
@ -1189,20 +1186,6 @@ int esp_ble_get_bond_device_num(void);
|
||||
*/
|
||||
esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_list);
|
||||
|
||||
/**
|
||||
* @brief This function is called to provide the OOB data for
|
||||
* SMP in response to ESP_GAP_BLE_OOB_REQ_EVT
|
||||
*
|
||||
* @param[in] bd_addr: BD address of the peer device.
|
||||
* @param[in] TK: TK value, the TK value shall be a 128-bit random number
|
||||
* @param[in] len: length of tk, should always be 128-bit
|
||||
*
|
||||
* @return - ESP_OK : success
|
||||
* - other : failed
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len);
|
||||
|
||||
#endif /* #if (SMP_INCLUDED == TRUE) */
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,6 @@
|
||||
#define CONFIG_MBEDTLS_PEM_WRITE_C 1
|
||||
#define CONFIG_BT_SPP_ENABLED 1
|
||||
#define CONFIG_BT_RESERVE_DRAM 0xdb5c
|
||||
#define CONFIG_APP_COMPILE_TIME_DATE 1
|
||||
#define CONFIG_CXX_EXCEPTIONS 1
|
||||
#define CONFIG_FATFS_FS_LOCK 0
|
||||
#define CONFIG_IP_LOST_TIMER_INTERVAL 120
|
||||
@ -72,20 +71,18 @@
|
||||
#define CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE 20
|
||||
#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1
|
||||
#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1
|
||||
#define CONFIG_CAMERA_CORE1 1
|
||||
#define CONFIG_MB_SERIAL_BUF_SIZE 256
|
||||
#define CONFIG_CONSOLE_UART_BAUDRATE 115200
|
||||
#define CONFIG_SPIRAM_SUPPORT 1
|
||||
#define CONFIG_LWIP_MAX_SOCKETS 10
|
||||
#define CONFIG_APP_ROLLBACK_ENABLE 1
|
||||
#define CONFIG_LWIP_NETIF_LOOPBACK 1
|
||||
#define CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT "pthread"
|
||||
#define CONFIG_EMAC_TASK_PRIORITY 20
|
||||
#define CONFIG_TIMER_TASK_STACK_DEPTH 2048
|
||||
#define CONFIG_TCP_MSS 1436
|
||||
#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1
|
||||
#define CONFIG_BTDM_CONTROLLER_MODE_BTDM 1
|
||||
#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF 3
|
||||
#define CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4 1
|
||||
#define CONFIG_TCPIP_TASK_AFFINITY_CPU0 1
|
||||
#define CONFIG_FATFS_CODEPAGE 850
|
||||
#define CONFIG_ULP_COPROC_RESERVE_MEM 512
|
||||
@ -121,12 +118,10 @@
|
||||
#define CONFIG_SPIRAM_BANKSWITCH_ENABLE 1
|
||||
#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1
|
||||
#define CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR 1
|
||||
#define CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER 1
|
||||
#define CONFIG_MB_SERIAL_TASK_STACK_SIZE 2048
|
||||
#define CONFIG_MBEDTLS_PSK_MODES 1
|
||||
#define CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO 1
|
||||
#define CONFIG_LWIP_DHCPS_LEASE_UNIT 60
|
||||
#define CONFIG_EFUSE_MAX_BLK_LEN 192
|
||||
#define CONFIG_SPIFFS_USE_MAGIC 1
|
||||
#define CONFIG_TCPIP_TASK_STACK_SIZE 2560
|
||||
#define CONFIG_BLUEDROID_PINNED_TO_CORE_0 1
|
||||
@ -138,6 +133,7 @@
|
||||
#define CONFIG_LWIP_MAX_ACTIVE_TCP 16
|
||||
#define CONFIG_TASK_WDT_TIMEOUT_S 5
|
||||
#define CONFIG_INT_WDT_TIMEOUT_MS 300
|
||||
#define CONFIG_SCCB_HARDWARE_I2C 1
|
||||
#define CONFIG_ESPTOOLPY_FLASHMODE "dio"
|
||||
#define CONFIG_BTC_TASK_STACK_SIZE 8192
|
||||
#define CONFIG_BLUEDROID_ENABLED 1
|
||||
@ -146,6 +142,7 @@
|
||||
#define CONFIG_ESPTOOLPY_BEFORE "default_reset"
|
||||
#define CONFIG_ADC2_DISABLE_DAC 1
|
||||
#define CONFIG_HFP_ENABLE 1
|
||||
#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_NUM 100
|
||||
#define CONFIG_LOG_DEFAULT_LEVEL 1
|
||||
#define CONFIG_TIMER_QUEUE_LENGTH 10
|
||||
#define CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT 1
|
||||
@ -164,7 +161,6 @@
|
||||
#define CONFIG_MDNS_MAX_SERVICES 10
|
||||
#define CONFIG_ULP_COPROC_ENABLED 1
|
||||
#define CONFIG_HFP_AUDIO_DATA_PATH_PCM 1
|
||||
#define CONFIG_IDF_TARGET_ESP32 1
|
||||
#define CONFIG_EMAC_CHECK_LINK_PERIOD_MS 2000
|
||||
#define CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL 1
|
||||
#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1
|
||||
@ -191,10 +187,8 @@
|
||||
#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1
|
||||
#define CONFIG_LWIP_SO_REUSE_RXTOALL 1
|
||||
#define CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT 20
|
||||
#define CONFIG_ESP32_WIFI_MGMT_SBUF_NUM 32
|
||||
#define CONFIG_PARTITION_TABLE_SINGLE_APP 1
|
||||
#define CONFIG_XTENSA_IMPL 1
|
||||
#define CONFIG_UNITY_ENABLE_FLOAT 1
|
||||
#define CONFIG_ESP32_WIFI_RX_BA_WIN 16
|
||||
#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1
|
||||
#define CONFIG_SPIFFS_USE_MTIME 1
|
||||
@ -238,7 +232,6 @@
|
||||
#define CONFIG_LOG_BOOTLOADER_LEVEL 0
|
||||
#define CONFIG_MBEDTLS_TLS_ENABLED 1
|
||||
#define CONFIG_LWIP_MAX_RAW_PCBS 16
|
||||
#define CONFIG_BTU_TASK_STACK_SIZE 4096
|
||||
#define CONFIG_SMP_ENABLE 1
|
||||
#define CONFIG_SPIRAM_SIZE -1
|
||||
#define CONFIG_MBEDTLS_SSL_SESSION_TICKETS 1
|
||||
@ -276,12 +269,9 @@
|
||||
#define CONFIG_MBEDTLS_TLS_CLIENT 1
|
||||
#define CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI 1
|
||||
#define CONFIG_BT_ENABLED 1
|
||||
#define CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY 1
|
||||
#define CONFIG_BT_SSP_ENABLED 1
|
||||
#define CONFIG_SW_COEXIST_PREFERENCE_BALANCE 1
|
||||
#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1
|
||||
#define CONFIG_MONITOR_BAUD 115200
|
||||
#define CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT -1
|
||||
#define CONFIG_ESP32_DEBUG_STUBS_ENABLE 1
|
||||
#define CONFIG_TCPIP_LWIP 1
|
||||
#define CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST 1
|
||||
@ -292,24 +282,22 @@
|
||||
#define CONFIG_MBEDTLS_HAVE_TIME 1
|
||||
#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1
|
||||
#define CONFIG_TCP_QUEUE_OOSEQ 1
|
||||
#define CONFIG_FATFS_ALLOC_PREFER_EXTRAM 1
|
||||
#define CONFIG_GATTS_ENABLE 1
|
||||
#define CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE 0
|
||||
#define CONFIG_ADC_CAL_EFUSE_VREF_ENABLE 1
|
||||
#define CONFIG_MBEDTLS_TLS_SERVER 1
|
||||
#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1
|
||||
#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_SUPPORTED 1
|
||||
#define CONFIG_FREERTOS_ISR_STACKSIZE 1536
|
||||
#define CONFIG_SUPPORT_TERMIOS 1
|
||||
#define CONFIG_CLASSIC_BT_ENABLED 1
|
||||
#define CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK 1
|
||||
#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK 1
|
||||
#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1
|
||||
#define CONFIG_IDF_TARGET "esp32"
|
||||
#define CONFIG_WL_SECTOR_SIZE_4096 1
|
||||
#define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1
|
||||
#define CONFIG_FREERTOS_NO_AFFINITY 0x7FFFFFFF
|
||||
#define CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED 1
|
||||
#define CONFIG_HTTPD_ERR_RESP_NO_DELAY 1
|
||||
#define CONFIG_MB_TIMER_INDEX 0
|
||||
#define CONFIG_SCAN_DUPLICATE_TYPE 0
|
||||
#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1
|
||||
@ -340,8 +328,10 @@
|
||||
#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200
|
||||
#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1
|
||||
#define CONFIG_ESPTOOLPY_PORT "/dev/cu.usbserial-DO00EAB0"
|
||||
#define CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS 1
|
||||
#define CONFIG_TASK_WDT_PANIC 1
|
||||
#define CONFIG_UNITY_ENABLE_DOUBLE 1
|
||||
#define CONFIG_OV3660_SUPPORT 1
|
||||
#define CONFIG_BLE_ADV_REPORT_DISCARD_THRSHOLD 20
|
||||
#define CONFIG_BLUEDROID_PINNED_TO_CORE 0
|
||||
#define CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG 1
|
||||
#define CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_ERROR 1
|
||||
|
@ -19,7 +19,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_types.h"
|
||||
#include "esp_intr.h"
|
||||
#include "esp_err.h"
|
||||
@ -106,7 +105,7 @@ extern "C" {
|
||||
#define CAN_EXTD_ID_MASK 0x1FFFFFFF /**< Bit mask for 29 bit Extended Frame Format ID */
|
||||
#define CAN_STD_ID_MASK 0x7FF /**< Bit mask for 11 bit Standard Frame Format ID */
|
||||
#define CAN_MAX_DATA_LEN 8 /**< Maximum number of data bytes in a CAN2.0B frame */
|
||||
#define CAN_IO_UNUSED ((gpio_num_t) -1) /**< Marks GPIO as unused in CAN configuration */
|
||||
#define CAN_IO_UNUSED (-1) /**< Marks GPIO as unused in CAN configuration */
|
||||
/** @endcond */
|
||||
|
||||
/* ----------------------- Enum and Struct Definitions ---------------------- */
|
||||
@ -393,34 +392,6 @@ esp_err_t can_initiate_recovery();
|
||||
*/
|
||||
esp_err_t can_get_status_info(can_status_info_t *status_info);
|
||||
|
||||
/**
|
||||
* @brief Clear the transmit queue
|
||||
*
|
||||
* This function will clear the transmit queue of all messages.
|
||||
*
|
||||
* @note The transmit queue is automatically cleared when can_stop() or
|
||||
* can_initiate_recovery() is called.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Transmit queue cleared
|
||||
* - ESP_ERR_INVALID_STATE: CAN driver is not installed or TX queue is disabled
|
||||
*/
|
||||
esp_err_t can_clear_transmit_queue();
|
||||
|
||||
/**
|
||||
* @brief Clear the receive queue
|
||||
*
|
||||
* This function will clear the receive queue of all messages.
|
||||
*
|
||||
* @note The receive queue is automatically cleared when can_start() is
|
||||
* called.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Transmit queue cleared
|
||||
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
|
||||
*/
|
||||
esp_err_t can_clear_receive_queue();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -189,14 +189,6 @@ typedef struct {
|
||||
int data_in_num; /*!< DATA in pin*/
|
||||
} i2s_pin_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S PDM RX downsample mode
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_PDM_DSR_8S = 0, /*!< downsampling number is 8 for PDM RX mode*/
|
||||
I2S_PDM_DSR_16S, /*!< downsampling number is 16 for PDM RX mode*/
|
||||
I2S_PDM_DSR_MAX,
|
||||
} i2s_pdm_dsr_t;
|
||||
|
||||
typedef intr_handle_t i2s_isr_handle_t;
|
||||
/**
|
||||
@ -223,25 +215,6 @@ typedef intr_handle_t i2s_isr_handle_t;
|
||||
*/
|
||||
esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin);
|
||||
|
||||
/**
|
||||
* @brief Set PDM mode down-sample rate
|
||||
* In PDM RX mode, there would be 2 rounds of downsample process in hardware.
|
||||
* In the first downsample process, the sampling number can be 16 or 8.
|
||||
* In the second downsample process, the sampling number is fixed as 8.
|
||||
* So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly.
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
* @param dsr i2s RX down sample rate for PDM mode.
|
||||
*
|
||||
* @note After calling this function, it would call i2s_set_clk inside to update the clock frequency.
|
||||
* Please call this function after I2S driver has been initialized.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM Out of memory
|
||||
*/
|
||||
esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr);
|
||||
|
||||
/**
|
||||
* @brief Set I2S dac mode, I2S built-in DAC is disabled by default
|
||||
*
|
||||
|
@ -80,19 +80,6 @@ typedef enum {
|
||||
RMT_CARRIER_LEVEL_MAX
|
||||
} rmt_carrier_level_t;
|
||||
|
||||
typedef enum {
|
||||
RMT_CHANNEL_UNINIT = 0, /*!< RMT channel uninitialized */
|
||||
RMT_CHANNEL_IDLE = 1, /*!< RMT channel status idle */
|
||||
RMT_CHANNEL_BUSY = 2, /*!< RMT channel status busy */
|
||||
} rmt_channel_status_t;
|
||||
|
||||
/**
|
||||
* @brief Data struct of RMT channel status
|
||||
*/
|
||||
typedef struct {
|
||||
rmt_channel_status_t status[RMT_CHANNEL_MAX]; /*!< Store the current status of each channel */
|
||||
} rmt_channel_status_result_t;
|
||||
|
||||
/**
|
||||
* @brief Data struct of RMT TX configure parameters
|
||||
*/
|
||||
@ -492,7 +479,6 @@ esp_err_t rmt_get_idle_level(rmt_channel_t channel, bool* idle_out_en, rmt_idle_
|
||||
*
|
||||
* @param channel RMT channel (0-7)
|
||||
* @param status Pointer to accept channel status.
|
||||
* Please refer to RMT_CHnSTATUS_REG(n=0~7) in `rmt_reg.h` for more details of each field.
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
@ -664,19 +650,6 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr
|
||||
*/
|
||||
esp_err_t rmt_driver_uninstall(rmt_channel_t channel);
|
||||
|
||||
/**
|
||||
* @brief Get the current status of eight channels.
|
||||
*
|
||||
* @note Do not call this function if it is possible that `rmt_driver_uninstall` will be called at the same time.
|
||||
*
|
||||
* @param[out] channel_status store the current status of each channel
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG Parameter is NULL
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t rmt_get_channel_status(rmt_channel_status_result_t *channel_status);
|
||||
|
||||
/**
|
||||
* @brief RMT send waveform from rmt_item array.
|
||||
*
|
||||
|
@ -101,32 +101,9 @@ typedef struct {
|
||||
* Call this if your driver wants to manage a SPI peripheral.
|
||||
*
|
||||
* @param host Peripheral to claim
|
||||
* @param source The caller indentification string.
|
||||
*
|
||||
* @return True if peripheral is claimed successfully; false if peripheral already is claimed.
|
||||
*/
|
||||
bool spicommon_periph_claim(spi_host_device_t host, const char* source);
|
||||
|
||||
// The macro is to keep the back-compatibility of IDF v3.2 and before
|
||||
// In this way we can call spicommon_periph_claim with two arguments, or the host with the source set to the calling function name
|
||||
// When two arguments (host, func) are given, __spicommon_periph_claim2 is called
|
||||
// or if only one arguments (host) is given, __spicommon_periph_claim1 is called
|
||||
#define spicommon_periph_claim(host...) __spicommon_periph_claim(host, 2, 1)
|
||||
#define __spicommon_periph_claim(host, source, n, ...) __spicommon_periph_claim ## n(host, source)
|
||||
#define __spicommon_periph_claim1(host, _) ({ \
|
||||
char* warning_str = "calling spicommon_periph_claim without source string is deprecated.";\
|
||||
spicommon_periph_claim(host, __FUNCTION__); })
|
||||
|
||||
#define __spicommon_periph_claim2(host, func) spicommon_periph_claim(host, func)
|
||||
|
||||
/**
|
||||
* @brief Check whether the spi periph is in use.
|
||||
*
|
||||
* @param host Peripheral to check.
|
||||
*
|
||||
* @return True if in use, otherwise false.
|
||||
*/
|
||||
bool spicommon_periph_in_use(spi_host_device_t host);
|
||||
bool spicommon_periph_claim(spi_host_device_t host);
|
||||
|
||||
/**
|
||||
* @brief Return the SPI peripheral so another driver can claim it.
|
||||
@ -147,15 +124,6 @@ bool spicommon_periph_free(spi_host_device_t host);
|
||||
*/
|
||||
bool spicommon_dma_chan_claim(int dma_chan);
|
||||
|
||||
/**
|
||||
* @brief Check whether the spi DMA channel is in use.
|
||||
*
|
||||
* @param dma_chan DMA channel to check.
|
||||
*
|
||||
* @return True if in use, otherwise false.
|
||||
*/
|
||||
bool spicommon_dma_chan_in_use(int dma_chan);
|
||||
|
||||
/**
|
||||
* @brief Return the SPI DMA channel so other driver can claim it, or just to power down DMA.
|
||||
*
|
||||
|
@ -168,10 +168,6 @@ typedef struct spi_device_t* spi_device_handle_t; ///< Handle for a device on a
|
||||
* @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in
|
||||
* DMA-capable memory.
|
||||
*
|
||||
* @warning The ISR of SPI is always executed on the core which calls this
|
||||
* function. Never starve the ISR on this core or the SPI transactions will not
|
||||
* be handled.
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if configuration is invalid
|
||||
* - ESP_ERR_INVALID_STATE if host already is in use
|
||||
|
@ -73,10 +73,7 @@ struct spi_slave_transaction_t {
|
||||
size_t length; ///< Total data length, in bits
|
||||
size_t trans_len; ///< Transaction data length, in bits
|
||||
const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase
|
||||
void *rx_buffer; /**< Pointer to receive buffer, or NULL for no MISO phase.
|
||||
* When the DMA is anabled, must start at WORD boundary (``rx_buffer%4==0``),
|
||||
* and has length of a multiple of 4 bytes.
|
||||
*/
|
||||
void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase
|
||||
void *user; ///< User-defined variable. Can be used to store eg transaction ID.
|
||||
};
|
||||
|
||||
@ -92,14 +89,10 @@ struct spi_slave_transaction_t {
|
||||
* it. The SPI hardware has two DMA channels to share. This parameter indicates which
|
||||
* one to use.
|
||||
*
|
||||
* @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in
|
||||
* @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in
|
||||
* DMA-capable memory.
|
||||
*
|
||||
* @warning The ISR of SPI is always executed on the core which calls this
|
||||
* function. Never starve the ISR on this core or the SPI transactions will not
|
||||
* be handled.
|
||||
*
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if configuration is invalid
|
||||
* - ESP_ERR_INVALID_STATE if host already is in use
|
||||
* - ESP_ERR_NO_MEM if out of memory
|
||||
@ -111,7 +104,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
||||
* @brief Free a SPI bus claimed as a SPI slave interface
|
||||
*
|
||||
* @param host SPI peripheral to free
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_INVALID_STATE if not all devices on the bus are freed
|
||||
* - ESP_OK on success
|
||||
@ -135,7 +128,7 @@ esp_err_t spi_slave_free(spi_host_device_t host);
|
||||
* into the transaction description.
|
||||
* @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to
|
||||
* never time out.
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
@ -145,19 +138,19 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact
|
||||
/**
|
||||
* @brief Get the result of a SPI transaction queued earlier
|
||||
*
|
||||
* This routine will wait until a transaction to the given device (queued earlier with
|
||||
* This routine will wait until a transaction to the given device (queued earlier with
|
||||
* spi_slave_queue_trans) has succesfully completed. It will then return the description of the
|
||||
* completed transaction so software can inspect the result and e.g. free the memory or
|
||||
* completed transaction so software can inspect the result and e.g. free the memory or
|
||||
* re-use the buffers.
|
||||
*
|
||||
* It is mandatory to eventually use this function for any transaction queued by ``spi_slave_queue_trans``.
|
||||
*
|
||||
* @param host SPI peripheral to that is acting as a slave
|
||||
* @param[out] trans_desc Pointer to variable able to contain a pointer to the description of the
|
||||
* @param[out] trans_desc Pointer to variable able to contain a pointer to the description of the
|
||||
* transaction that is executed
|
||||
* @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
|
||||
* out.
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
@ -168,16 +161,16 @@ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transacti
|
||||
* @brief Do a SPI transaction
|
||||
*
|
||||
* Essentially does the same as spi_slave_queue_trans followed by spi_slave_get_trans_result. Do
|
||||
* not use this when there is still a transaction queued that hasn't been finalized
|
||||
* not use this when there is still a transaction queued that hasn't been finalized
|
||||
* using spi_slave_get_trans_result.
|
||||
*
|
||||
* @param host SPI peripheral to that is acting as a slave
|
||||
* @param trans_desc Pointer to variable able to contain a pointer to the description of the
|
||||
* @param trans_desc Pointer to variable able to contain a pointer to the description of the
|
||||
* transaction that is executed. Not const because we may want to write status back
|
||||
* into the transaction description.
|
||||
* @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
|
||||
* out.
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
|
@ -801,7 +801,7 @@ esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag);
|
||||
* light sleep. This function allows setting the threshold value.
|
||||
*
|
||||
* Stop bit and parity bits (if enabled) also contribute to the number of edges.
|
||||
* For example, letter 'a' with ASCII code 97 is encoded as 0100001101 on the wire
|
||||
* For example, letter 'a' with ASCII code 97 is encoded as 010001101 on the wire
|
||||
* (with 8n1 configuration), start and stop bits included. This sequence has 3
|
||||
* positive edges (transitions from 0 to 1). Therefore, to wake up the system
|
||||
* when 'a' is sent, set wakeup_threshold=3.
|
||||
@ -813,10 +813,7 @@ esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag);
|
||||
* correct baud rate all the time, select REF_TICK as UART clock source,
|
||||
* by setting use_ref_tick field in uart_config_t to true.
|
||||
*
|
||||
* @note in ESP32, the wakeup signal can only be input via IO_MUX (i.e.
|
||||
* GPIO3 should be configured as function_1 to wake up UART0,
|
||||
* GPIO9 should be configured as function_5 to wake up UART1), UART2
|
||||
* does not support light sleep wakeup feature.
|
||||
* @note in ESP32, UART2 does not support light sleep wakeup feature.
|
||||
*
|
||||
* @param uart_num UART number
|
||||
* @param wakeup_threshold number of RX edges for light sleep wakeup, value is 3 .. 0x3ff.
|
||||
|
@ -1,362 +0,0 @@
|
||||
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _ESP_EFUSE_MANAGER_H_
|
||||
#define _ESP_EFUSE_MANAGER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define ESP_ERR_EFUSE 0x1600 /*!< Base error code for efuse api. */
|
||||
#define ESP_OK_EFUSE_CNT (ESP_ERR_EFUSE + 0x01) /*!< OK the required number of bits is set. */
|
||||
#define ESP_ERR_EFUSE_CNT_IS_FULL (ESP_ERR_EFUSE + 0x02) /*!< Error field is full. */
|
||||
#define ESP_ERR_EFUSE_REPEATED_PROG (ESP_ERR_EFUSE + 0x03) /*!< Error repeated programming of programmed bits is strictly forbidden. */
|
||||
#define ESP_ERR_CODING (ESP_ERR_EFUSE + 0x04) /*!< Error while a encoding operation. */
|
||||
|
||||
/**
|
||||
* @brief Type of eFuse blocks
|
||||
*/
|
||||
typedef enum {
|
||||
EFUSE_BLK0 = 0, /**< Number of eFuse block. Reserved. */
|
||||
EFUSE_BLK1 = 1, /**< Number of eFuse block. Used for Flash Encryption. If not using that Flash Encryption feature, they can be used for another purpose. */
|
||||
EFUSE_BLK2 = 2, /**< Number of eFuse block. Used for Secure Boot. If not using that Secure Boot feature, they can be used for another purpose. */
|
||||
EFUSE_BLK3 = 3 /**< Number of eFuse block. Uses for the purpose of the user. */
|
||||
} esp_efuse_block_t;
|
||||
|
||||
/**
|
||||
* @brief Type of coding scheme
|
||||
*/
|
||||
typedef enum {
|
||||
EFUSE_CODING_SCHEME_NONE = 0, /**< None */
|
||||
EFUSE_CODING_SCHEME_3_4 = 1, /**< 3/4 coding */
|
||||
EFUSE_CODING_SCHEME_REPEAT = 2, /**< Repeat coding */
|
||||
} esp_efuse_coding_scheme_t;
|
||||
|
||||
/**
|
||||
* @brief Structure eFuse field
|
||||
*/
|
||||
typedef struct {
|
||||
esp_efuse_block_t efuse_block: 8; /**< Block of eFuse */
|
||||
uint8_t bit_start; /**< Start bit [0..255] */
|
||||
uint16_t bit_count; /**< Length of bit field [1..-]*/
|
||||
} esp_efuse_desc_t;
|
||||
|
||||
/**
|
||||
* @brief Reads bits from EFUSE field and writes it into an array.
|
||||
*
|
||||
* The number of read bits will be limited to the minimum value
|
||||
* from the description of the bits in "field" structure or "dst_size_bits" required size.
|
||||
* Use "esp_efuse_get_field_size()" function to determine the length of the field.
|
||||
* @param[in] field A pointer to the structure describing the fields of efuse.
|
||||
* @param[out] dst A pointer to array that will contain the result of reading.
|
||||
* @param[in] dst_size_bits The number of bits required to read.
|
||||
* If the requested number of bits is greater than the field,
|
||||
* the number will be limited to the field size.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
*/
|
||||
esp_err_t esp_efuse_read_field_blob(const esp_efuse_desc_t* field[], void* dst, size_t dst_size_bits);
|
||||
|
||||
/**
|
||||
* @brief Reads bits from EFUSE field and returns number of bits programmed as "1".
|
||||
*
|
||||
* If the bits are set not sequentially, they will still be counted.
|
||||
* @param[in] field A pointer to the structure describing the fields of efuse.
|
||||
* @param[out] out_cnt A pointer that will contain the number of programmed as "1" bits.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
*/
|
||||
esp_err_t esp_efuse_read_field_cnt(const esp_efuse_desc_t* field[], size_t* out_cnt);
|
||||
|
||||
/**
|
||||
* @brief Writes array to EFUSE field.
|
||||
*
|
||||
* The number of write bits will be limited to the minimum value
|
||||
* from the description of the bits in "field" structure or "src_size_bits" required size.
|
||||
* Use "esp_efuse_get_field_size()" function to determine the length of the field.
|
||||
* After the function is completed, the writing registers are cleared.
|
||||
* @param[in] field A pointer to the structure describing the fields of efuse.
|
||||
* @param[in] src A pointer to array that contains the data for writing.
|
||||
* @param[in] src_size_bits The number of bits required to write.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_EFUSE_REPEATED_PROG: Error repeated programming of programmed bits is strictly forbidden.
|
||||
* - ESP_ERR_CODING: Error range of data does not match the coding scheme.
|
||||
*/
|
||||
esp_err_t esp_efuse_write_field_blob(const esp_efuse_desc_t* field[], const void* src, size_t src_size_bits);
|
||||
|
||||
/**
|
||||
* @brief Writes a required count of bits as "1" to EFUSE field.
|
||||
*
|
||||
* If there are no free bits in the field to set the required number of bits to "1",
|
||||
* ESP_ERR_EFUSE_CNT_IS_FULL error is returned, the field will not be partially recorded.
|
||||
* After the function is completed, the writing registers are cleared.
|
||||
* @param[in] field A pointer to the structure describing the fields of efuse.
|
||||
* @param[in] cnt Required number of programmed as "1" bits.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_EFUSE_CNT_IS_FULL: Not all requested cnt bits is set.
|
||||
*/
|
||||
esp_err_t esp_efuse_write_field_cnt(const esp_efuse_desc_t* field[], size_t cnt);
|
||||
|
||||
/**
|
||||
* @brief Sets a write protection for the whole block.
|
||||
*
|
||||
* After that, it is impossible to write to this block.
|
||||
* The write protection does not apply to block 0.
|
||||
* @param[in] blk Block number of eFuse. (EFUSE_BLK1, EFUSE_BLK2 and EFUSE_BLK3)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_EFUSE_CNT_IS_FULL: Not all requested cnt bits is set.
|
||||
* - ESP_ERR_NOT_SUPPORTED: The block does not support this command.
|
||||
*/
|
||||
esp_err_t esp_efuse_set_write_protect(esp_efuse_block_t blk);
|
||||
|
||||
/**
|
||||
* @brief Sets a read protection for the whole block.
|
||||
*
|
||||
* After that, it is impossible to read from this block.
|
||||
* The read protection does not apply to block 0.
|
||||
* @param[in] blk Block number of eFuse. (EFUSE_BLK1, EFUSE_BLK2 and EFUSE_BLK3)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_EFUSE_CNT_IS_FULL: Not all requested cnt bits is set.
|
||||
* - ESP_ERR_NOT_SUPPORTED: The block does not support this command.
|
||||
*/
|
||||
esp_err_t esp_efuse_set_read_protect(esp_efuse_block_t blk);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of bits used by field.
|
||||
*
|
||||
* @param[in] field A pointer to the structure describing the fields of efuse.
|
||||
*
|
||||
* @return Returns the number of bits used by field.
|
||||
*/
|
||||
int esp_efuse_get_field_size(const esp_efuse_desc_t* field[]);
|
||||
|
||||
/**
|
||||
* @brief Returns value of efuse register.
|
||||
*
|
||||
* This is a thread-safe implementation.
|
||||
* Example: EFUSE_BLK2_RDATA3_REG where (blk=2, num_reg=3)
|
||||
* @param[in] blk Block number of eFuse.
|
||||
* @param[in] num_reg The register number in the block.
|
||||
*
|
||||
* @return Value of register
|
||||
*/
|
||||
uint32_t esp_efuse_read_reg(esp_efuse_block_t blk, unsigned int num_reg);
|
||||
|
||||
/**
|
||||
* @brief Write value to efuse register.
|
||||
*
|
||||
* Apply a coding scheme if necessary.
|
||||
* This is a thread-safe implementation.
|
||||
* Example: EFUSE_BLK3_WDATA0_REG where (blk=3, num_reg=0)
|
||||
* @param[in] blk Block number of eFuse.
|
||||
* @param[in] num_reg The register number in the block.
|
||||
* @param[in] val Value to write.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_EFUSE_REPEATED_PROG: Error repeated programming of programmed bits is strictly forbidden.
|
||||
*/
|
||||
esp_err_t esp_efuse_write_reg(esp_efuse_block_t blk, unsigned int num_reg, uint32_t val);
|
||||
|
||||
/**
|
||||
* @brief Return efuse coding scheme for blocks.
|
||||
*
|
||||
* Note: The coding scheme is applicable only to 1, 2 and 3 blocks. For 0 block, the coding scheme is always ``NONE``.
|
||||
*
|
||||
* @param[in] blk Block number of eFuse.
|
||||
* @return Return efuse coding scheme for blocks
|
||||
*/
|
||||
esp_efuse_coding_scheme_t esp_efuse_get_coding_scheme(esp_efuse_block_t blk);
|
||||
|
||||
/**
|
||||
* @brief Read key to efuse block starting at the offset and the required size.
|
||||
*
|
||||
* @param[in] blk Block number of eFuse.
|
||||
* @param[in] dst_key A pointer to array that will contain the result of reading.
|
||||
* @param[in] offset_in_bits Start bit in block.
|
||||
* @param[in] size_bits The number of bits required to read.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_CODING: Error range of data does not match the coding scheme.
|
||||
*/
|
||||
esp_err_t esp_efuse_read_block(esp_efuse_block_t blk, void* dst_key, size_t offset_in_bits, size_t size_bits);
|
||||
|
||||
/**
|
||||
* @brief Write key to efuse block starting at the offset and the required size.
|
||||
*
|
||||
* @param[in] blk Block number of eFuse.
|
||||
* @param[in] src_key A pointer to array that contains the key for writing.
|
||||
* @param[in] offset_in_bits Start bit in block.
|
||||
* @param[in] size_bits The number of bits required to write.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The operation was successfully completed.
|
||||
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
|
||||
* - ESP_ERR_CODING: Error range of data does not match the coding scheme.
|
||||
* - ESP_ERR_EFUSE_REPEATED_PROG: Error repeated programming of programmed bits
|
||||
*/
|
||||
esp_err_t esp_efuse_write_block(esp_efuse_block_t blk, const void* src_key, size_t offset_in_bits, size_t size_bits);
|
||||
|
||||
/**
|
||||
* @brief Returns chip version from efuse
|
||||
*
|
||||
* @return chip version
|
||||
*/
|
||||
uint8_t esp_efuse_get_chip_ver(void);
|
||||
|
||||
/**
|
||||
* @brief Returns chip package from efuse
|
||||
*
|
||||
* @return chip package
|
||||
*/
|
||||
uint32_t esp_efuse_get_pkg_ver(void);
|
||||
|
||||
/* @brief Permanently update values written to the efuse write registers
|
||||
*
|
||||
* After updating EFUSE_BLKx_WDATAx_REG registers with new values to
|
||||
* write, call this function to permanently write them to efuse.
|
||||
*
|
||||
* @note Setting bits in efuse is permanent, they cannot be unset.
|
||||
*
|
||||
* @note Due to this restriction you don't need to copy values to
|
||||
* Efuse write registers from the matching read registers, bits which
|
||||
* are set in the read register but unset in the matching write
|
||||
* register will be unchanged when new values are burned.
|
||||
*
|
||||
* @note This function is not threadsafe, if calling code updates
|
||||
* efuse values from multiple tasks then this is caller's
|
||||
* responsibility to serialise.
|
||||
*
|
||||
* After burning new efuses, the read registers are updated to match
|
||||
* the new efuse values.
|
||||
*/
|
||||
void esp_efuse_burn_new_values(void);
|
||||
|
||||
/* @brief Reset efuse write registers
|
||||
*
|
||||
* Efuse write registers are written to zero, to negate
|
||||
* any changes that have been staged here.
|
||||
*
|
||||
* @note This function is not threadsafe, if calling code updates
|
||||
* efuse values from multiple tasks then this is caller's
|
||||
* responsibility to serialise.
|
||||
*/
|
||||
void esp_efuse_reset(void);
|
||||
|
||||
/* @brief Disable BASIC ROM Console via efuse
|
||||
*
|
||||
* By default, if booting from flash fails the ESP32 will boot a
|
||||
* BASIC console in ROM.
|
||||
*
|
||||
* Call this function (from bootloader or app) to permanently
|
||||
* disable the console on this chip.
|
||||
*/
|
||||
void esp_efuse_disable_basic_rom_console(void);
|
||||
|
||||
/* @brief Encode one or more sets of 6 byte sequences into
|
||||
* 8 bytes suitable for 3/4 Coding Scheme.
|
||||
*
|
||||
* This function is only useful if the CODING_SCHEME efuse
|
||||
* is set to value 1 for 3/4 Coding Scheme.
|
||||
*
|
||||
* @param[in] in_bytes Pointer to a sequence of bytes to encode for 3/4 Coding Scheme. Must have length in_bytes_len. After being written to hardware, these bytes will read back as little-endian words.
|
||||
* @param[out] out_words Pointer to array of words suitable for writing to efuse write registers. Array must contain 2 words (8 bytes) for every 6 bytes in in_bytes_len. Can be a pointer to efuse write registers.
|
||||
* @param in_bytes_len. Length of array pointed to by in_bytes, in bytes. Must be a multiple of 6.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if either pointer is null or in_bytes_len is not a multiple of 6. ESP_OK otherwise.
|
||||
*/
|
||||
esp_err_t esp_efuse_apply_34_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len);
|
||||
|
||||
/* @brief Write random data to efuse key block write registers
|
||||
*
|
||||
* @note Caller is responsible for ensuring efuse
|
||||
* block is empty and not write protected, before calling.
|
||||
*
|
||||
* @note Behaviour depends on coding scheme: a 256-bit key is
|
||||
* generated and written for Coding Scheme "None", a 192-bit key
|
||||
* is generated, extended to 256-bits by the Coding Scheme,
|
||||
* and then writtten for 3/4 Coding Scheme.
|
||||
*
|
||||
* @note This function does not burn the new values, caller should
|
||||
* call esp_efuse_burn_new_values() when ready to do this.
|
||||
*
|
||||
* @param blk_wdata0_reg Address of the first data write register
|
||||
* in the block
|
||||
*/
|
||||
void esp_efuse_write_random_key(uint32_t blk_wdata0_reg);
|
||||
|
||||
/* @brief Return secure_version from efuse field.
|
||||
* @return Secure version from efuse field
|
||||
*/
|
||||
uint32_t esp_efuse_read_secure_version();
|
||||
|
||||
/* @brief Check secure_version from app and secure_version and from efuse field.
|
||||
*
|
||||
* @param secure_version Secure version from app.
|
||||
* @return
|
||||
* - True: If version of app is equal or more then secure_version from efuse.
|
||||
*/
|
||||
bool esp_efuse_check_secure_version(uint32_t secure_version);
|
||||
|
||||
/* @brief Write efuse field by secure_version value.
|
||||
*
|
||||
* Update the secure_version value is available if the coding scheme is None.
|
||||
* Note: Do not use this function in your applications. This function is called as part of the other API.
|
||||
*
|
||||
* @param[in] secure_version Secure version from app.
|
||||
* @return
|
||||
* - ESP_OK: Successful.
|
||||
* - ESP_FAIL: secure version of app cannot be set to efuse field.
|
||||
* - ESP_ERR_NOT_SUPPORTED: Anti rollback is not supported with the 3/4 and Repeat coding scheme.
|
||||
*/
|
||||
esp_err_t esp_efuse_update_secure_version(uint32_t secure_version);
|
||||
|
||||
/* @brief Initializes variables: offset and size to simulate the work of an eFuse.
|
||||
*
|
||||
* Note: To simulate the work of an eFuse need to set CONFIG_EFUSE_SECURE_VERSION_EMULATE option
|
||||
* and to add in the partition.csv file a line `efuse_em, data, efuse, , 0x2000,`.
|
||||
*
|
||||
* @param[in] offset The starting address of the partition where the eFuse data will be located.
|
||||
* @param[in] size The size of the partition.
|
||||
*/
|
||||
void esp_efuse_init(uint32_t offset, uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ESP_EFUSE_MANAGER_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user