8 Commits

Author SHA1 Message Date
h2zero
90bd5740ff Release v1.3.3 2022-02-15 20:26:02 -07:00
h2zero
7b93b0f763 Fix memory leak when deleting client instance. 2022-02-14 21:14:14 -07:00
h2zero
11e705f487 Remove recursive calling when fetching remote attributes. 2022-02-14 21:12:20 -07:00
h2zero
aeaa432553 Retrieve attributes with 16bit uuid if 128bit fails.
When retrieving attribute handles using the 128bit base version of a 16 bit UUID some devices will
report "not found". This will attempt to convert the UUID to the 16bit version and try again.

* Adds `to16()` method to NimBLEUUID.
2022-02-14 21:10:42 -07:00
h2zero
cbebbd0dc0 Fix IDF version check. 2022-02-14 21:10:01 -07:00
h2zero
89bf89f153 Fix memory leak when services changed. 2022-02-14 21:09:42 -07:00
h2zero
8cc01bb20a Update docs 2022-02-14 21:09:29 -07:00
h2zero
cee369fd92 Cleanup ARM-gcc compiler warnings. 2022-02-14 21:09:23 -07:00
63 changed files with 410 additions and 5208 deletions

View File

@@ -12,10 +12,6 @@ elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble
)
elseif("bt" IN_LIST BUILD_COMPONENTS OR "__bt" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
bt
)
endif()
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino")
@@ -24,7 +20,15 @@ if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf
)
endif()
set(srcs "src/NimBLE2904.cpp"
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
@@ -35,7 +39,6 @@ set(srcs "src/NimBLE2904.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
@@ -45,28 +48,7 @@ set(srcs "src/NimBLE2904.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp")
if(CONFIG_BT_NIMBLE_MESH)
list(APPEND srcs "src/NimBLEMeshCreateModel.c"
"src/NimBLEMeshElement.cpp"
"src/NimBLEMeshModel.cpp"
"src/NimBLEMeshNode.cpp")
endif()
if(CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
list(APPEND srcs "src/mesh_config_store/config/config_store.c")
endif()
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS "${srcs}"
"src/NimBLEUUID.cpp"
REQUIRES
bt
nvs_flash
@@ -74,7 +56,3 @@ idf_component_register(
${ESP_NIMBLE_PRIV_REQUIRES}
)
if(CONFIG_BT_NIMBLE_MESH AND CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
idf_build_set_property(COMPILE_OPTIONS "-DMYNEWT_VAL_BLE_MESH_SETTINGS=1" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-I${COMPONENT_DIR}/src/mesh_config_store" APPEND)
endif()

33
Kconfig
View File

@@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
@@ -47,34 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
bool "Enable timestamps to be stored with attribute values."
default "n"
help
Enabling this option will store the timestamp when an attribute value is updated.
This allows for checking the last update time using getTimeStamp()
or getValue(time_t*). If disabled, the timestamp returned from these functions will be 0.
Disabling timestamps will reduce the memory used for each value.
config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
int "Initial attribute value size (bytes) for empty values."
range 1 512
default 20
help
Sets the default allocation size (bytes) for each attribute if not specified
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
config NIMBLE_CPP_PERSIST_MESH_SETTINGS
bool "Enable persistent storage of mesh config settings."
default "n"
depends on BT_NIMBLE_MESH
help
Enabling this option will store the provisioning and app key settings
in non-volatile storage when using NimBLE Mesh.
endmenu

View File

@@ -1,29 +0,0 @@
# Bluetooth 5.x features
## About extended advertising
Extended advertising allows for much more capability and flexibility.
* Allows for 251 bytes of advertisement data and up to 1650 bytes when chained (configuration dependant) vs 31.
* New PHY's (physical layers) that allow for faster data rate (2M PHY) or long range/slower data rates (CODED PHY) as well as the original 1M PHY.
* New periodic advertising, allowing the scanning device to sync with the advertisements of a beacon. This allows for the scanning device to sleep or perform other tasks before the next expected advertisement is sent, preserving cpu cycles and power (To be implemented).
<br>
## Enabling extended advertising
Extended advertising is supported when enabled with the config option `CONFIG_BT_NIMBLE_EXT_ADV` set to a value of 1. This is done in menuconfig under `Component config > Bluetooth > NimBLE options >
Enable extended advertising`.
When enabled the following will occur:
* `NimBLEScan::start` method will scan on both the 1M PHY and the coded PHY standards automatically.
* `NimBLEClient::connect` will use the primary PHY the device is listening on, unless specified (see below).
* `NimBLEClient::setConnectPhy` becomes available to specify the PHY's to connect with (default is all).
* `NimBLEAdvertising` is no longer available for use and is replaced by `NimBLEExtAdvertising`. `NimBLEDevice::getAdvertising` will now return an instance of `NimBLEExtAdvertising`.
* `NimBLEAdvertisementData` is no longer available for use and is replaced by `NimBLEExtAdvertisement`. This new class is where everything about the advertisement is configured, including the advertisement intervals and advertisement ended callback.

View File

@@ -6,24 +6,6 @@ Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br/>
`CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED`
Enable/disable storing the timestamp when an attribute value is updated
This allows for checking the last update time using getTimeStamp() or getValue(time_t*)
If disabled, the timestamp returned from these functions will be 0.
Disabling timestamps will reduce the memory used for each value.
1 = Enabled, 0 = Disabled; Default = Disabled
<br/>
`CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH`
Set the default allocation size (bytes) for each attribute.
If not specified when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
<br/>
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
Sets the default MTU size.

View File

@@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_client)

View File

@@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_client
include $(IDF_PATH)/make/project.mk

View File

@@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -1,169 +0,0 @@
/** NimBLE Extended Client Demo:
*
* Demonstrates the Bluetooth 5.x client capabilities.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" void app_main(void);
void scanEndedCB(NimBLEScanResults results);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
static NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10; /* 0 = scan forever */
/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ;
/* Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
};
void onDisconnect(NimBLEClient* pClient) {
printf("%s Disconnected - Starting scan\n", pClient->getPeerAddress().toString().c_str());
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
};
};
/* Define a class to handle the callbacks when advertisements are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD")))
{
printf("Found Our Service\n");
/* Ready to connect now */
doConnect = true;
/* Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/* stop scan before connecting */
NimBLEDevice::getScan()->stop();
}
};
};
/* Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
printf("Scan Ended\n");
if (!doConnect) { /* Don't start the scan while connecting */
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
}
}
/* Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallbacks, false);
/* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
* Combine these with OR ("|"), eg BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK;
*/
pClient->setConnectPhy(connectPhys);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
pClient->setConnectTimeout(10);
if (!pClient->connect(advDevice)) {
/* Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n");
return false;
}
printf("Connected to: %s RSSI: %d\n",
pClient->getPeerAddress().toString().c_str(),
pClient->getRssi());
/* Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
pSvc = pClient->getService(SERVICE_UUID);
if (pSvc) {
pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if (pChr) {
// Read the value of the characteristic.
if (pChr->canRead()) {
std::string value = pChr->readValue();
printf("Characteristic value: %s\n", value.c_str());
}
}
} else {
printf("ABCD service not found.\n");
}
NimBLEDevice::deleteClient(pClient);
printf("Done with this device!\n");
return true;
}
void connectTask (void * parameter){
/* Loop here until we find a device we want to connect to */
for (;;) {
if (doConnect) {
/* Found a device we want to connect to, do it now */
if (connectToServer()) {
printf("Success!, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
doConnect = false;
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelete(NULL);
}
void app_main (void) {
printf("Starting NimBLE Client\n");
/* Create a task to handle connecting to peers */
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
/* Initialize NimBLE, no device name specified as we are not advertising */
NimBLEDevice::init("");
NimBLEScan* pScan = NimBLEDevice::getScan();
/* create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
/* Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime, scanEndedCB);
printf("Scanning for peripherals\n");
}

View File

@@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_server)

View File

@@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_server
include $(IDF_PATH)/make/project.mk

View File

@@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -1,139 +0,0 @@
/** NimBLE Extended Server Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise a long data string on the CODED and 1M Phy's and
* starts a server allowing connection over either PHY's. It will advertise for
* 5 seconds then sleep for 20 seconds, if a client connects it will sleep once
* it has disconnected then repeats.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepSeconds = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
};
void onDisconnect(NimBLEServer* pServer) {
printf("Client disconnected - sleeping for %u seconds\n", sleepSeconds);
esp_deep_sleep_start();
};
};
/* Callback class to handle advertising events */
class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf("Client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %u seconds\n", sleepSeconds);
break;
default:
break;
}
esp_deep_sleep_start();
}
};
void app_main (void) {
NimBLEDevice::init("Extended advertiser");
/* Create the server and add the services/characteristics/descriptors */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the services */
pService->start();
/*
* Create an extended advertisement with the instance ID 0 and set the PHY's.
* Multiple instances can be added as long as the instance ID is incremented.
*/
NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy);
/* Set the advertisement as connectable */
extAdv.setConnectable(true);
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extAdv.setScannable(false); // The default is false, set here for demonstration.
/* Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */
extAdv.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Extended Advertising Demo.\r\n"
"Extended advertising allows for "
"251 bytes of data in a single advertisement,\r\n"
"or up to 1650 bytes with chaining.\r\n"
"This example message is 226 bytes long "
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks for advertising events */
pAdvertising->setCallbacks(new advertisingCallbacks);
/*
* NimBLEExtAdvertising::setInstanceData takes the instance ID and
* a reference to a `NimBLEExtAdvertisement` object. This sets the data
* that will be advertised for this instance ID, returns true if successful.
*
* Note: It is safe to create the advertisement as a local variable if setInstanceData
* is called before exiting the code block as the data will be copied.
*/
if (pAdvertising->setInstanceData(0, extAdv)) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);
}

View File

@@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_multi_advertiser)

View File

@@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_multi_advertiser
include $(IDF_PATH)/make/project.mk

View File

@@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -1,170 +0,0 @@
/** NimBLE Multi Advertiser Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise 2 advertisements, and extended scannable instance
* and a connectable legacy instance. They will advertise for 5 seconds then
* sleep for 20 seconds. The extended scannable instance will use the scan
* request callback to update it's data when a scan response is requested.
*
* Created: on April 9 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepTime = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
};
void onDisconnect(NimBLEServer* pServer) {
printf("Client disconnected\n");
// if still advertising we won't sleep yet.
if (!pServer->getAdvertising()->isAdvertising()) {
printf("Sleeping for %u seconds\n", sleepTime);
esp_deep_sleep_start();
}
};
};
/* Callback class to handle advertising events */
class advCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf(" client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %u seconds\n", sleepTime);
break;
default:
break;
}
esp_deep_sleep_start();
}
bool m_updatedSR = false;
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) {
printf("Scan request for instance %u\n", inst_id);
// if the data has already been updated we don't need to change it again.
if (!m_updatedSR) {
printf("Updating scan data\n");
NimBLEExtAdvertisement sr;
sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!"));
pAdv->setScanResponseData(inst_id, sr);
m_updatedSR = true;
}
}
};
void app_main (void) {
NimBLEDevice::init("Multi advertiser");
/* Create a server for our legacy advertiser */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the service */
pService->start();
/* Create our multi advertising instances */
// extended scannable instance advertising on coded and 1m PHY's.
NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
// Legacy advertising as a connectable device.
NimBLEExtAdvertisement legacyConnectable;
// Optional scan response data.
NimBLEExtAdvertisement legacyScanResponse;
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extScannable.setScannable(true);
extScannable.setConnectable(false);
/* Set the initial data */
extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!"));
/* enable the scan response callback, we will use this to update the data. */
extScannable.enableScanRequestCallback(true);
/* Optional custom address for this advertisment. */
legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
/* Set the advertising data. */
legacyConnectable.setName("Legacy");
legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* Set the legacy and connectable flags. */
legacyConnectable.setLegacyAdvertising(true);
legacyConnectable.setConnectable(true);
/* Put some data in the scan response if desired. */
legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
/* Get the advertising ready */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(new advCallbacks);
/* Set instance data.
* Up to 5 instances can be used if configured in menuconfig, instance 0 is always available.
*
* We will set the extended scannable data on instance 0 and the legacy data on instance 1.
* Note that the legacy scan response data needs to be set to the same instance (1).
*/
if (pAdvertising->setInstanceData( 0, extScannable ) &&
pAdvertising->setInstanceData( 1, legacyConnectable ) &&
pAdvertising->setScanResponseData( 1, legacyScanResponse )) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepTime * 1000000);
}

View File

@@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(basic_on_off_level_server)

View File

@@ -1,3 +0,0 @@
PROJECT_NAME := basic_on_off_level_server
include $(IDF_PATH)/make/project.mk

View File

@@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -1,64 +0,0 @@
#include "NimBLEDevice.h"
#include "driver/gpio.h"
extern "C" {void app_main(void);}
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
// LED pins
#define LEDR GPIO_NUM_16
#define LEDG GPIO_NUM_17
#define LEDB GPIO_NUM_18
#define OUTPUT_PIN (1ULL<<LEDG)
static uint8_t onOffVal = 0;
static int16_t levelVal = 0;
class onOffSrvModelCallbacks : public NimBLEMeshModelCallbacks {
void setOnOff(NimBLEMeshModel *pModel, uint8_t val) {
printf("on/off set val %d, transition time: %dms\n", val, pModel->getTransTime());
onOffVal = val;
gpio_set_level(LEDG, !onOffVal);
}
uint8_t getOnOff(NimBLEMeshModel *pModel) {
printf("on/off get val %d\n", onOffVal);
return onOffVal;
}
};
class levelSrvModelCallbacks : public NimBLEMeshModelCallbacks {
void setLevel(NimBLEMeshModel *pModel, int16_t val) {
printf("Level set val %d, transition time: %dms\n", val, pModel->getTransTime());
levelVal = val;
}
int16_t getLevel(NimBLEMeshModel *pModel) {
printf("Level get val %d\n", levelVal);
return levelVal;
}
};
void app_main(void) {
gpio_config_t io_conf;
io_conf.intr_type = (gpio_int_type_t)GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = OUTPUT_PIN;
io_conf.pull_down_en = (gpio_pulldown_t)0;
io_conf.pull_up_en = (gpio_pullup_t)0;
gpio_config(&io_conf);
gpio_set_level(LEDG, 1);
NimBLEDevice::init("");
NimBLEMeshNode *pMesh = NimBLEDevice::createMeshNode(NimBLEUUID(SERVICE_UUID),0);
NimBLEMeshElement* pElem = pMesh->getElement();
NimBLEMeshModel* pModel = pElem->createModel(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, new onOffSrvModelCallbacks());
pModel->setValue(onOffVal);
//pElem = pMesh->createElement();
pModel = pElem->createModel(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, new levelSrvModelCallbacks());
pModel->setValue(levelVal);
pMesh->start();
printf("Mesh Started");
}

View File

@@ -20,8 +20,6 @@
#include "NimBLEUtils.h"
#include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLEAdvertisedDevice";
@@ -71,7 +69,7 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device.
*/
uint16_t NimBLEAdvertisedDevice::getAppearance() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -89,7 +87,7 @@ uint16_t NimBLEAdvertisedDevice::getAppearance() {
* @return The advertisement interval in 0.625ms units.
*/
uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -107,7 +105,7 @@ uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
* @return The preferred min connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMinInterval() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -125,7 +123,7 @@ uint16_t NimBLEAdvertisedDevice::getMinInterval() {
* @return The preferred max connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -143,7 +141,7 @@ uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
* @return The manufacturer data of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getManufacturerData() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -161,7 +159,7 @@ std::string NimBLEAdvertisedDevice::getManufacturerData() {
* @return The URI data.
*/
std::string NimBLEAdvertisedDevice::getURI() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -179,7 +177,7 @@ std::string NimBLEAdvertisedDevice::getURI() {
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
@@ -216,7 +214,7 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
* @brief Get the number of target addresses.
* @return The number of addresses.
*/
uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() {
size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
uint8_t count = 0;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
@@ -234,7 +232,7 @@ uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() {
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t count = 0;
size_t data_loc = ULONG_MAX;
uint8_t data_loc = 0xFF;
index++;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
@@ -244,7 +242,7 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
}
if(count > 0 && data_loc != ULONG_MAX) {
if(count > 0 && data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
@@ -266,9 +264,9 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
size_t data_loc = findServiceData(index, &bytes);
uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != ULONG_MAX) {
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > bytes) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
@@ -288,9 +286,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
uint8_t index = 0;
size_t data_loc = findServiceData(index, &bytes);
size_t plSize = m_payload.size() - 2;
uint8_t data_loc = findServiceData(index, &bytes);
uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t plSize = m_payload.size() - 2;
while(data_loc < plSize) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -315,9 +313,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
size_t data_loc = findServiceData(index, &bytes);
uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != ULONG_MAX) {
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length >= bytes) {
return NimBLEUUID(field->value, bytes, false);
@@ -332,10 +330,10 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
* @brief Find the service data at the index.
* @param [in] index The index of the service data to find.
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
* @return The index in the vector where the data is located, ULONG_MAX if not found.
* @return The index in the vector where the data is located, 0xFF if not found.
*/
size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
size_t data_loc = 0;
uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
uint8_t data_loc = 0;
uint8_t found = 0;
*bytes = 0;
@@ -360,7 +358,7 @@ size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
return data_loc;
}
return ULONG_MAX;
return 0xFF;
}
@@ -368,7 +366,7 @@ size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
* @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector.
*/
uint8_t NimBLEAdvertisedDevice::getServiceDataCount() {
size_t NimBLEAdvertisedDevice::getServiceDataCount() {
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
@@ -386,7 +384,7 @@ uint8_t NimBLEAdvertisedDevice::getServiceDataCount() {
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
uint8_t count = 0;
size_t data_loc = 0;
uint8_t data_loc = 0;
uint8_t uuidBytes = 0;
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
ble_hs_adv_field *field = nullptr;
@@ -433,7 +431,7 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @brief Get the number of services advertised
* @return The count of services in the advertising packet.
*/
uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
@@ -469,7 +467,7 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
* @return The TX Power of the advertised device.
*/
int8_t NimBLEAdvertisedDevice::getTXPower() {
size_t data_loc = 0;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -583,60 +581,17 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
} // haveTXPower
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Get the set ID of the extended advertisement.
* @return The set ID.
*/
uint8_t NimBLEAdvertisedDevice::getSetId() {
return m_sid;
} // getSetId
/**
* @brief Get the primary PHY used by this advertisement.
* @return The PHY type, one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
*/
uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() {
return m_primPhy;
} // getPrimaryPhy
/**
* @brief Get the primary PHY used by this advertisement.
* @return The PHY type, one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() {
return m_secPhy;
} // getSecondaryPhy
/**
* @brief Get the periodic interval of the advertisement.
* @return The periodic advertising interval, 0 if not periodic advertising.
*/
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() {
return m_periodicItvl;
} // getPeriodicInterval
#endif
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t * data_loc) {
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
ble_hs_adv_field *field = nullptr;
size_t length = m_payload.size();
size_t data = 0;
uint8_t count = 0;
uint8_t data = 0;
uint8_t length = m_payload.size();
uint8_t count = 0;
if (length < 3) {
if(length < 2) {
return count;
}
while (length > 2) {
while (length > 1) {
field = (ble_hs_adv_field*)&m_payload[data];
if (field->length >= length) {
@@ -644,7 +599,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t
}
if (field->type == type) {
switch (type) {
switch(type) {
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
case BLE_HS_ADV_TYPE_COMP_UUIDS16:
count += field->length / 2;
@@ -670,8 +625,8 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t
break;
}
if (data_loc != nullptr) {
if (index == 0 || count >= index) {
if(data_loc != nullptr) {
if(index == 0 || count >= index) {
break;
}
}
@@ -681,7 +636,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t
data += 1 + field->length;
}
if (data_loc != nullptr && field != nullptr) {
if(data_loc != nullptr && field != nullptr) {
*data_loc = data;
}
@@ -702,13 +657,8 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
* @brief Set the adFlag for this device.
* @param [in] advType The advertisement flag data from the advertisement.
*/
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType, bool isLegacyAdv) {
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
m_advType = advType;
#if CONFIG_BT_NIMBLE_EXT_ADV
m_isLegacyAdv = isLegacyAdv;
#else
(void)isLegacyAdv;
#endif
} // setAdvType
@@ -753,10 +703,10 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val;
}
if (haveServiceData()) {
uint8_t count = getServiceDataCount();
if(haveServiceData()) {
size_t count = getServiceDataCount();
res += "\nService Data:";
for(uint8_t i = 0; i < count; i++) {
for(size_t i = 0; i < count; i++) {
res += "\nUUID: " + std::string(getServiceDataUUID(i));
res += ", Data: " + getServiceData(i);
}
@@ -831,34 +781,5 @@ size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payload.size();
} // getPayloadLength
/**
* @brief Check if this device is advertising as connectable.
* @return True if the device is connectable.
*/
bool NimBLEAdvertisedDevice::isConnectable() {
#if CONFIG_BT_NIMBLE_EXT_ADV
if (m_isLegacyAdv) {
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND ||
m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
}
#endif
return (m_advType & BLE_HCI_ADV_CONN_MASK) ||
(m_advType & BLE_HCI_ADV_DIRECT_MASK);
} // isConnectable
/**
* @brief Check if this advertisement is a legacy or extended type
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
*/
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() {
#if CONFIG_BT_NIMBLE_EXT_ADV
return m_isLegacyAdv;
# else
return true;
#endif
} // isLegacyAdvertisement
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@@ -71,7 +71,7 @@ public:
std::string getName();
int getRSSI();
NimBLEScan* getScan();
uint8_t getServiceDataCount();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid);
@@ -111,9 +111,9 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
uint8_t getServiceUUIDCount();
size_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
uint8_t getTargetAddressCount();
size_t getTargetAddressCount();
int8_t getTXPower();
uint8_t* getPayload();
uint8_t getAdvLength();
@@ -133,30 +133,16 @@ public:
bool haveTargetAddress();
bool haveURI();
std::string toString();
bool isConnectable();
bool isLegacyAdvertisement();
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId();
uint8_t getPrimaryPhy();
uint8_t getSecondaryPhy();
uint16_t getPeriodicInterval();
#endif
private:
friend class NimBLEScan;
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType, bool isLegacyAdv);
void setAdvType(uint8_t advType);
void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
#if CONFIG_BT_NIMBLE_EXT_ADV
void setSetId(uint8_t sid) { m_sid = sid; }
void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
void setSecondaryPhy(uint8_t phy) { m_secPhy = phy; }
void setPeriodicInterval(uint16_t itvl) { m_periodicItvl = itvl; }
#endif
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t * data_loc = nullptr);
size_t findServiceData(uint8_t index, uint8_t* bytes);
uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
uint8_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
@@ -164,13 +150,6 @@ private:
time_t m_timestamp;
bool m_callbackSent;
uint8_t m_advLength;
#if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv;
uint8_t m_sid;
uint8_t m_primPhy;
uint8_t m_secPhy;
uint16_t m_periodicItvl;
#endif
std::vector<uint8_t> m_payload;
};

View File

@@ -14,9 +14,7 @@
*
*/
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h"
@@ -414,7 +412,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// If already advertising just return
if(ble_gap_adv_active()) {
NIMBLE_LOGW(LOG_TAG, "Advertising already active");
return true;
return false;
}
// Save the duration incase of host reset so we can restart with the same params
@@ -627,7 +625,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent,
(void*)this);
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
@@ -636,10 +634,6 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
case 0:
break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break;
@@ -662,26 +656,24 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
return (rc == 0 || rc == BLE_HS_EALREADY);
return (rc == 0);
} // start
/**
* @brief Stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEAdvertising::stop() {
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
return;
}
NIMBLE_LOGD(LOG_TAG, "<< stop");
return true;
} // stop
@@ -1034,4 +1026,4 @@ std::string NimBLEAdvertisementData::getPayload() {
return m_payload;
} // getPayload
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */

View File

@@ -15,9 +15,7 @@
#ifndef MAIN_BLEADVERTISING_H_
#define MAIN_BLEADVERTISING_H_
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h"
@@ -91,7 +89,7 @@ public:
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
bool stop();
void stop();
void setAppearance(uint16_t appearance);
void setName(const std::string &name);
void setManufacturerData(const std::string &data);
@@ -113,7 +111,6 @@ public:
private:
friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
@@ -137,5 +134,5 @@ private:
std::vector<uint8_t> m_uri;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */
#endif /* MAIN_BLEADVERTISING_H_ */

View File

@@ -1,447 +0,0 @@
/*
* NimBLEAttValue.h
*
* Created: on March 18, 2021
* Author H2zero
*
*/
#ifndef MAIN_NIMBLEATTVALUE_H_
#define MAIN_NIMBLEATTVALUE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
#include <Arduino.h>
#endif
#include "NimBLELog.h"
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include <string>
#include <vector>
#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
#endif
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# include <time.h>
#endif
#if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
#endif
/* Used to determine if the type passed to a template has a c_str() and length() method. */
template <typename T, typename = void, typename = void>
struct Has_c_str_len : std::false_type {};
template <typename T>
struct Has_c_str_len<T, decltype(void(std::declval<T &>().c_str())),
decltype(void(std::declval<T &>().length()))> : std::true_type {};
/**
* @brief A specialized container class to hold BLE attribute values.
* @details This class is designed to be more memory efficient than using\n
* standard container types for value storage, while being convertable to\n
* many different container classes.
*/
class NimBLEAttValue
{
uint8_t* m_attr_value = nullptr;
uint16_t m_attr_max_len = 0;
uint16_t m_attr_len = 0;
uint16_t m_capacity = 0;
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp = 0;
#endif
void deepCopy(const NimBLEAttValue & source);
public:
/**
* @brief Default constructor.
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initial value from a buffer.
* @param value A pointer to the initial value to set.
* @param[in] len The size in bytes of the value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const uint8_t *value, uint16_t len,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initializer list.
* @param list An initializer list containing the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(std::initializer_list<uint8_t> list,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(list.begin(), (uint16_t)list.size(), max_len){}
/**
* @brief Construct with an initial value from a const char string.
* @param value A pointer to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){}
/**
* @brief Construct with an initial value from a std::string.
* @param str A std::string containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.data(), (uint16_t)str.length(), max_len){}
/**
* @brief Construct with an initial value from a std::vector<uint8_t>.
* @param vec A std::vector<uint8_t> containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::vector<uint8_t> vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(&vec[0], (uint16_t)vec.size(), max_len){}
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/**
* @brief Construct with an initial value from an Arduino String.
* @param str An Arduino String containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.c_str(), str.length(), max_len){}
#endif
/** @brief Copy constructor */
NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); }
/** @brief Move constructor */
NimBLEAttValue(NimBLEAttValue && source) { *this = std::move(source); }
/** @brief Destructor */
~NimBLEAttValue();
/** @brief Returns the max size in bytes */
uint16_t max_size() const { return m_attr_max_len; }
/** @brief Returns the currently allocated capacity in bytes */
uint16_t capacity() const { return m_capacity; }
/** @brief Returns the current length of the value in bytes */
uint16_t length() const { return m_attr_len; }
/** @brief Returns the current size of the value in bytes */
uint16_t size() const { return m_attr_len; }
/** @brief Returns a pointer to the internal buffer of the value */
const uint8_t* data() const { return m_attr_value; }
/** @brief Returns a pointer to the internal buffer of the value as a const char* */
const char* c_str() const { return (const char*)m_attr_value; }
/** @brief Iterator begin */
const uint8_t* begin() const { return m_attr_value; }
/** @brief Iterator end */
const uint8_t* end() const { return m_attr_value + m_attr_len; }
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
/** @brief Returns a timestamp of when the value was last updated */
time_t getTimeStamp() const { return m_timestamp; }
/** @brief Set the timestamp to the current time */
void setTimeStamp() { m_timestamp = time(nullptr); }
/**
* @brief Set the timestamp to the specified time
* @param[in] t The timestamp value to set
*/
void setTimeStamp(time_t t) { m_timestamp = t; }
#else
time_t getTimeStamp() const { return 0; }
void setTimeStamp() { }
void setTimeStamp(time_t t) { }
#endif
/**
* @brief Set the value from a buffer
* @param[in] value A ponter to a buffer containing the value.
* @param[in] len The length of the value in bytes.
* @returns True if successful.
*/
bool setValue(const uint8_t *value, uint16_t len);
/**
* @brief Set value to the value of const char*.
* @param [in] s A ponter to a const char value to set.
*/
bool setValue(const char* s) {
return setValue((uint8_t*)s, (uint16_t)strlen(s)); }
/**
* @brief Get a pointer to the value buffer with timestamp.
* @param[in] timestamp A ponter to a time_t variable to store the timestamp.
* @returns A pointer to the internal value buffer.
*/
const uint8_t* getValue(time_t *timestamp);
/**
* @brief Append data to the value.
* @param[in] value A ponter to a data buffer with the value to append.
* @param[in] len The length of the value to append in bytes.
* @returns A reference to the appended NimBLEAttValue.
*/
NimBLEAttValue& append(const uint8_t *value, uint16_t len);
/*********************** Template Functions ************************/
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @details Only used for types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T &s) {
return setValue((uint8_t*)&s, sizeof(T));
}
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T & s) {
return setValue((uint8_t*)s.c_str(), (uint16_t)s.length());
}
/**
* @brief Template to return the value as a <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than\n
* <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is\n
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && size() < sizeof(T)) {
return T();
}
return *((T *)getValue(timestamp));
}
/*********************** Operators ************************/
/** @brief Subscript operator */
uint8_t operator [](int pos) const {
assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; }
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const {
return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
/** @brief Operator; Get the value as a std::string. */
operator std::string() const {
return std::string((char*)m_attr_value, m_attr_len); }
/** @brief Operator; Get the value as a const uint8_t*. */
operator const uint8_t*() const { return m_attr_value; }
/** @brief Operator; Append another NimBLEAttValue. */
NimBLEAttValue& operator +=(const NimBLEAttValue & source) {
return append(source.data(), source.size()); }
/** @brief Operator; Set the value from a std::string source. */
NimBLEAttValue& operator =(const std::string & source) {
setValue((uint8_t*)source.data(), (uint16_t)source.size()); return *this; }
/** @brief Move assignment operator */
NimBLEAttValue& operator =(NimBLEAttValue && source);
/** @brief Copy assignment operator */
NimBLEAttValue& operator =(const NimBLEAttValue & source);
/** @brief Equality operator */
bool operator ==(const NimBLEAttValue & source) {
return (m_attr_len == source.size()) ?
memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; }
/** @brief Inequality operator */
bool operator !=(const NimBLEAttValue & source){ return !(*this == source); }
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/** @brief Operator; Get the value as an Arduino String value. */
operator String() const { return String((char*)m_attr_value); }
#endif
};
inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) {
m_attr_value = (uint8_t*)calloc(init_len + 1, 1);
assert(m_attr_value && "No Mem");
m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len);
m_attr_len = 0;
m_capacity = init_len;
setTimeStamp(0);
}
inline NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
}
inline NimBLEAttValue::~NimBLEAttValue() {
if(m_attr_value != nullptr) {
free(m_attr_value);
}
}
inline NimBLEAttValue& NimBLEAttValue::operator =(NimBLEAttValue && source) {
if (this != &source){
free(m_attr_value);
m_attr_value = source.m_attr_value;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
source.m_attr_value = nullptr;
}
return *this;
}
inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) {
if (this != &source) {
deepCopy(source);
}
return *this;
}
inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) {
uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1);
assert(res && "deepCopy: realloc failed");
ble_npl_hw_enter_critical();
m_attr_value = res;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1);
ble_npl_hw_exit_critical(0);
}
inline const uint8_t* NimBLEAttValue::getValue(time_t *timestamp) {
if(timestamp != nullptr) {
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
#else
*timestamp = 0;
#endif
}
return m_attr_value;
}
inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) {
if (len > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "value exceeds max, len=%u, max=%u",
len, m_attr_max_len);
return false;
}
uint8_t *res = m_attr_value;
if (len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (len + 1));
m_capacity = len;
}
assert(res && "setValue: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return true;
}
inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len) {
if (len < 1) {
return *this;
}
if ((m_attr_len + len) > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u",
len, m_attr_max_len);
return *this;
}
uint8_t* res = m_attr_value;
uint16_t new_len = m_attr_len + len;
if (new_len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (new_len + 1));
m_capacity = new_len;
}
assert(res && "append: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value + m_attr_len, value, len);
m_attr_len = new_len;
m_attr_value[m_attr_len] = '\0';
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return *this;
}
#endif /*(CONFIG_BT_ENABLED) */
#endif /* MAIN_NIMBLEATTVALUE_H_ */

View File

@@ -30,29 +30,26 @@ static const char* LOG_TAG = "NimBLECharacteristic";
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties,
uint16_t max_len, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) {
}
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties,
uint16_t max_len, NimBLEService* pService)
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
m_value = "";
m_timestamp = 0;
m_removed = 0;
} // NimBLECharacteristic
@@ -234,14 +231,17 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
/**
* @brief Retrieve the current value of the characteristic.
* @return The NimBLEAttValue containing the current characteristic value.
* @return A std::string containing the current characteristic value.
*/
NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
std::string NimBLECharacteristic::getValue(time_t *timestamp) {
ble_npl_hw_enter_critical();
std::string retVal = m_value;
if(timestamp != nullptr) {
m_value.getValue(timestamp);
*timestamp = m_timestamp;
}
ble_npl_hw_exit_critical(0);
return m_value;
return retVal;
} // getValue
@@ -250,7 +250,10 @@ NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
* @return The length of the current characteristic data.
*/
size_t NimBLECharacteristic::getDataLength() {
return m_value.size();
ble_npl_hw_enter_critical();
size_t len = m_value.length();
ble_npl_hw_exit_critical(0);
return len;
}
@@ -283,26 +286,25 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
}
ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size());
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(),
pCharacteristic->m_value.length());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
uint16_t att_max_len = pCharacteristic->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > att_max_len) {
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
@@ -382,58 +384,36 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
/**
* @brief Send an indication.
* @brief Send an indication.\n
* An indication is a transmission of up to the first 20 bytes of the characteristic value.\n
* An indication will block waiting for a positive confirmation from the client.
*/
void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate
/**
* @brief Send an indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
*/
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) {
notify(value, length, false);
} // indicate
/**
* @brief Send an indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
*/
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value) {
notify(value.data(), value.size(), false);
} // indicate
/**
* @brief Send a notification or indication.
* @brief Send a notification.\n
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
* A notification will not block; it is a fire and forget.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(bool is_notification) {
notify(m_value.data(), m_value.length(), is_notification);
} // notify
notify(getValue(), is_notification);
}
/**
* @brief Send a notification or indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
* @brief Send a notification.\n
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
* A notification will not block; it is a fire and forget.
* @param[in] value An optional value to send as the notification, else the current characteristic value is used.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, bool is_notification) {
notify(value.data(), value.size(), is_notification);
} // notify
/**
* @brief Send a notification or indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification) {
void NimBLECharacteristic::notify(std::string value, bool is_notification) {
size_t length = value.length();
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
@@ -492,7 +472,7 @@ void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_n
// don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call.
os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
@@ -536,30 +516,40 @@ NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
/**
* @brief Set the value of the characteristic from a data buffer .
* @param [in] data The data buffer to set for the characteristic.
* @param [in] length The number of bytes in the data buffer.
* @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 NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s",
length, pHex, getUUID().toString().c_str());
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);
#endif
m_value.setValue(data, length);
if (length > BLE_ATT_ATTR_MAX_LEN) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return;
}
time_t t = time(nullptr);
ble_npl_hw_enter_critical();
m_value = std::string((char*)data, length);
m_timestamp = t;
ble_npl_hw_exit_critical(0);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from a `std::vector<uint8_t>`.\n
* @param [in] vec The std::vector<uint8_t> reference to set the characteristic value from.
* @brief Set the value of the characteristic from string data.\n
* We set the value of the characteristic from the bytes contained in the string.
* @param [in] value the std::string value of the characteristic.
*/
void NimBLECharacteristic::setValue(const std::vector<uint8_t>& vec) {
return setValue((uint8_t*)&vec[0], vec.size());
}// setValue
void NimBLECharacteristic::setValue(const std::string &value) {
setValue((uint8_t*)(value.data()), value.length());
} // setValue
/**

View File

@@ -44,7 +44,6 @@ typedef enum {
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "NimBLEAttValue.h"
#include <string>
#include <vector>
@@ -66,13 +65,11 @@ public:
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
@@ -80,93 +77,66 @@ public:
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLECharacteristicCallbacks*
getCallbacks();
void indicate();
void indicate(const uint8_t* value, size_t length);
void indicate(const std::vector<uint8_t>& value);
void notify(bool is_notification = true);
void notify(const uint8_t* value, size_t length, bool is_notification = true);
void notify(const std::vector<uint8_t>& value, bool is_notification = true);
void notify(std::string value, bool is_notification = true);
size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
void addDescriptor(NimBLEDescriptor *pDescriptor);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false);
NimBLEService* getService();
uint16_t getProperties();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
std::string getValue(time_t *timestamp = nullptr);
size_t getDataLength();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::vector<uint8_t>& vec);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);;
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristicCallbacks* getCallbacks();
/*********************** Template Functions ************************/
/**
* @brief Template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) { m_value.setValue<T>(s); }
/**
* @brief Template to convert the characteristic data to <type\>.
* @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/**
* @brief Template to send a notification from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
* @param[in] value The <type\>value to set.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @details Only used if the <type\> has a `c_str()` method.
* @brief Convenience template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
#ifdef _DOXYGEN_
void
#else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
notify(const T& value, bool is_notification = true) {
notify((uint8_t*)value.c_str(), value.length(), is_notification);
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
/**
* @brief Template to send an indication from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
* @param[in] value The <type\>value to set.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
void
#else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
indicate(const T& value) {
indicate((uint8_t*)value.c_str(), value.length());
}
NimBLEService* getService();
uint16_t getProperties();
private:
@@ -183,8 +153,9 @@ private:
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
NimBLEAttValue m_value;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
time_t m_timestamp;
uint8_t m_removed;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
@@ -217,7 +188,7 @@ public:
ERROR_INDICATE_FAILURE
}Status;
virtual ~NimBLECharacteristicCallbacks();
virtual ~NimBLECharacteristicCallbacks();
virtual void onRead(NimBLECharacteristic* pCharacteristic);
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onWrite(NimBLECharacteristic* pCharacteristic);

View File

@@ -65,11 +65,6 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pTaskData = nullptr;
m_connEstablished = false;
m_lastErr = 0;
#if CONFIG_BT_NIMBLE_EXT_ADV
m_phyMask = BLE_GAP_LE_PHY_1M_MASK |
BLE_GAP_LE_PHY_2M_MASK |
BLE_GAP_LE_PHY_CODED_MASK;
#endif
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@@ -225,22 +220,9 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
do {
#if CONFIG_BT_NIMBLE_EXT_ADV
rc = ble_gap_ext_connect(NimBLEDevice::m_own_addr_type,
&peerAddr_t,
m_connectTimeout,
m_phyMask,
&m_pConnParams,
&m_pConnParams,
&m_pConnParams,
NimBLEClient::handleGapEvent,
this);
#else
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
#endif
switch (rc) {
case 0:
break;
@@ -415,21 +397,6 @@ int NimBLEClient::disconnect(uint8_t reason) {
} // disconnect
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Set the PHY types to use when connecting to a server.
* @param [in] mask A bitmask indicating what PHYS to connect with.\n
* The available bits are:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
*/
void NimBLEClient::setConnectPhy(uint8_t mask) {
m_phyMask = mask;
}
#endif
/**
* @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
@@ -801,7 +768,7 @@ int NimBLEClient::serviceDiscoveredCB(
if(error->status == BLE_HS_EDONE) {
pTaskData->rc = 0;
} else {
NIMBLE_LOGE(LOG_TAG, "serviceDiscoveredCB() rc=%d %s",
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
error->status,
NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
@@ -809,7 +776,7 @@ int NimBLEClient::serviceDiscoveredCB(
xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG,"<< Service Discovered");
NIMBLE_LOGD(LOG_TAG,"<< << Service Discovered");
return error->status;
}
@@ -820,11 +787,11 @@ int NimBLEClient::serviceDiscoveredCB(
* @param [in] characteristicUUID The characteristic whose value we wish to read.
* @returns characteristic value or an empty string if not found
*/
NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
NimBLEAttValue ret;
std::string ret = "";
NimBLERemoteService* pService = getService(serviceUUID);
if(pService != nullptr) {
@@ -834,7 +801,7 @@ NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBL
}
}
NIMBLE_LOGD(LOG_TAG, "<< getValue");
NIMBLE_LOGD(LOG_TAG, "<<getValue");
return ret;
} // getValue
@@ -848,7 +815,7 @@ NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBL
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const NimBLEAttValue &value, bool response)
const std::string &value, bool response)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@@ -909,7 +876,7 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] arg A pointer to the client instance that registered for this callback.
*/
/*STATIC*/
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
int rc;
@@ -1028,7 +995,11 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
(*characteristic)->toString().c_str());
uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om);
(*characteristic)->m_value.setValue(event->notify_rx.om->om_data, data_len);
time_t t = time(nullptr);
ble_npl_hw_enter_critical();
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, data_len);
(*characteristic)->m_timestamp = t;
ble_npl_hw_exit_critical(0);
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",

View File

@@ -21,7 +21,6 @@
#include "NimBLEUUID.h"
#include "NimBLEUtils.h"
#include "NimBLEConnInfo.h"
#include "NimBLEAttValue.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h"
@@ -52,9 +51,9 @@ public:
NimBLERemoteService* getService(const NimBLEUUID &uuid);
void deleteServices();
size_t deleteService(const NimBLEUUID &uuid);
NimBLEAttValue getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const NimBLEAttValue &value, bool response = false);
const std::string &value, bool response = false);
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
@@ -73,9 +72,6 @@ public:
void discoverAttributes();
NimBLEConnInfo getConnInfo();
int getLastError();
#if CONFIG_BT_NIMBLE_EXT_ADV
void setConnectPhy(uint8_t mask);
#endif
private:
NimBLEClient(const NimBLEAddress &peerAddress);
@@ -101,9 +97,6 @@ private:
NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t m_phyMask;
#endif
std::vector<NimBLERemoteService*> m_servicesVector;

View File

@@ -28,32 +28,27 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
/**
* @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
* @brief NimBLEDescriptor constructor.
*/
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {
NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) {
}
/**
* @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
* @brief NimBLEDescriptor constructor.
*/
NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic)
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
{
m_uuid = 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 = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_properties = 0;
m_removed = 0;
@@ -89,6 +84,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
* @brief NimBLEDescriptor destructor.
*/
NimBLEDescriptor::~NimBLEDescriptor() {
free(m_value.attr_value); // Release the storage we created in the constructor.
} // ~NimBLEDescriptor
/**
@@ -105,7 +101,7 @@ uint16_t NimBLEDescriptor::getHandle() {
* @return The length (in bytes) of the value of this descriptor.
*/
size_t NimBLEDescriptor::getLength() {
return m_value.size();
return m_value.attr_len;
} // getLength
@@ -119,14 +115,10 @@ NimBLEUUID NimBLEDescriptor::getUUID() {
/**
* @brief Get the value of this descriptor.
* @return The NimBLEAttValue of this descriptor.
* @return A pointer to the value of this descriptor.
*/
NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
if (timestamp != nullptr) {
m_value.getValue(timestamp);
}
return m_value;
uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value;
} // getValue
@@ -135,7 +127,7 @@ NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
* @return A std::string instance containing a copy of the descriptor's value.
*/
std::string NimBLEDescriptor::getStringValue() {
return std::string(m_value);
return std::string((char *) m_value.attr_value, m_value.attr_len);
}
@@ -171,25 +163,23 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
}
ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size());
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
uint16_t att_max_len = pDescriptor->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
uint8_t buf[pDescriptor->m_value.attr_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > att_max_len) {
if((len + next->om_len) > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
@@ -241,19 +231,27 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
* @param [in] length The length of the data in bytes.
*/
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
m_value.setValue(data, length);
if (length > m_value.attr_max_len) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return;
}
ble_npl_hw_enter_critical();
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
ble_npl_hw_exit_critical(0);
} // setValue
/**
* @brief Set the value of the descriptor from a `std::vector<uint8_t>`.\n
* @param [in] vec The std::vector<uint8_t> reference to set the descriptor value from.
* @brief Set the value of the descriptor.
* @param [in] value The value of the descriptor in string form.
*/
void NimBLEDescriptor::setValue(const std::vector<uint8_t>& vec) {
return setValue((uint8_t*)&vec[0], vec.size());
void NimBLEDescriptor::setValue(const std::string &value) {
setValue((uint8_t*) value.data(), value.length());
} // setValue
/**
* @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptior belongs to.

View File

@@ -20,10 +20,17 @@
#include "NimBLECharacteristic.h"
#include "NimBLEUUID.h"
#include "NimBLEAttValue.h"
#include <string>
typedef struct
{
uint16_t attr_max_len; /*!< attribute max value length */
uint16_t attr_len; /*!< attribute current value length */
uint8_t *attr_value; /*!< the pointer to attribute value */
} attr_value_t;
class NimBLEService;
class NimBLECharacteristic;
class NimBLEDescriptorCallbacks;
@@ -47,36 +54,24 @@ public:
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
NimBLECharacteristic* getCharacteristic();
size_t getLength();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
uint8_t* getValue();
std::string getStringValue();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::vector<uint8_t>& vec);
/*********************** Template Functions ************************/
void setValue(const std::string &value);
NimBLECharacteristic* getCharacteristic();
/**
* @brief Template to set the characteristic value to <type\>val.
* @brief Convenience template to set the descriptor value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) { m_value.setValue<T>(s); }
/**
* @brief Template to convert the descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
private:
@@ -94,7 +89,7 @@ private:
NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties;
NimBLEAttValue m_value;
attr_value_t m_value;
uint8_t m_removed;
}; // NimBLEDescriptor

View File

@@ -69,15 +69,9 @@ NimBLEServer* NimBLEDevice::m_pServer = nullptr;
uint32_t NimBLEDevice::m_passkey = 123456;
bool NimBLEDevice::m_synced = false;
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# else
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# endif
#endif
NimBLEMeshNode* NimBLEDevice::m_pMeshNode = nullptr;
gap_event_handler NimBLEDevice::m_customGapHandler = nullptr;
ble_gap_event_listener NimBLEDevice::m_listener;
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -92,30 +86,6 @@ uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN
uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
#endif
/**
* @brief Create a new mesh node.
* @param [in] uuid The uuid to advertise before being provisioned.
* @param [in] type A bitmask of the node type to create.
* @return A point to new instance of the mesh node.
*/
NimBLEMeshNode* NimBLEDevice::createMeshNode(NimBLEUUID uuid, uint8_t type) {
if(m_pMeshNode == nullptr) {
m_pMeshNode = new NimBLEMeshNode(uuid, type);
}
return m_pMeshNode;
}
/**
* @brief Get the mesh node instance.
* @return a pointer to the mesh node instance or nullptr if no node exists.
*/
NimBLEMeshNode* NimBLEDevice::getMeshNode() {
return m_pMeshNode;
}
/**
* @brief Create a new instance of a server.
* @return A new instance of the server.
@@ -144,45 +114,6 @@ NimBLEMeshNode* NimBLEDevice::getMeshNode() {
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Get the instance of the advertising object.
* @return A pointer to the advertising object.
*/
NimBLEExtAdvertising* NimBLEDevice::getAdvertising() {
if(m_bleAdvertising == nullptr) {
m_bleAdvertising = new NimBLEExtAdvertising();
}
return m_bleAdvertising;
}
/**
* @brief Convenience function to begin advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
*/
bool NimBLEDevice::startAdvertising(uint8_t inst_id,
int duration,
int max_events) {
return getAdvertising()->start(inst_id, duration, max_events);
} // startAdvertising
/**
* @brief Convenience function to stop advertising a data set.
* @param [in] inst_id The extended advertisement instance ID to stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEDevice::stopAdvertising(uint8_t inst_id) {
return getAdvertising()->stop(inst_id);
} // stopAdvertising
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Get the instance of the advertising object.
* @return A pointer to the advertising object.
@@ -197,19 +128,17 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() {
/**
* @brief Convenience function to begin advertising.
* @return True if advertising started successfully.
*/
bool NimBLEDevice::startAdvertising() {
return getAdvertising()->start();
void NimBLEDevice::startAdvertising() {
getAdvertising()->start();
} // startAdvertising
# endif
/**
* @brief Convenience function to stop all advertising.
* @return True if advertising stopped successfully.
* @brief Convenience function to stop advertising.
*/
bool NimBLEDevice::stopAdvertising() {
return getAdvertising()->stop();
void NimBLEDevice::stopAdvertising() {
getAdvertising()->stop();
} // stopAdvertising
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)

View File

@@ -23,11 +23,7 @@
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
# endif
#include "NimBLEAdvertising.h"
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -38,8 +34,6 @@
#include "NimBLEServer.h"
#endif
#include "NimBLEMeshNode.h"
#include "NimBLEUtils.h"
#include "NimBLESecurity.h"
#include "NimBLEAddress.h"
@@ -117,9 +111,6 @@ public:
static NimBLEServer* getServer();
#endif
static NimBLEMeshNode* createMeshNode(NimBLEUUID uuid, uint8_t type);
static NimBLEMeshNode* getMeshNode();
#ifdef ESP_PLATFORM
static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT);
static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT);
@@ -148,18 +139,9 @@ public:
static void removeIgnored(const NimBLEAddress &address);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* getAdvertising();
static bool startAdvertising(uint8_t inst_id,
int duration = 0,
int max_events = 0);
static bool stopAdvertising(uint8_t inst_id);
static bool stopAdvertising();
# else
static NimBLEAdvertising* getAdvertising();
static bool startAdvertising();
static bool stopAdvertising();
# endif
static NimBLEAdvertising* getAdvertising();
static void startAdvertising();
static void stopAdvertising();
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -196,10 +178,6 @@ private:
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
friend class NimBLEAdvertising;
# if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisement;
# endif
#endif
static void onReset(int reason);
@@ -216,11 +194,7 @@ private:
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* m_bleAdvertising;
# else
static NimBLEAdvertising* m_bleAdvertising;
# endif
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -237,7 +211,6 @@ private:
static uint8_t m_scanFilterMode;
#endif
static std::vector<NimBLEAddress> m_whiteList;
static NimBLEMeshNode* m_pMeshNode;
};

View File

@@ -1,870 +0,0 @@
/*
* NimBLEExtAdvertising.cpp
*
* Created: on February 6, 2022
* Author H2zero
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#endif
#include "NimBLEExtAdvertising.h"
#include "NimBLEDevice.h"
#include "NimBLEServer.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
static NimBLEExtAdvertisingCallbacks defaultCallbacks;
static const char* LOG_TAG = "NimBLEExtAdvertising";
/**
* @brief Destructor: deletes callback instances if requested.
*/
NimBLEExtAdvertising::~NimBLEExtAdvertising() {
if(m_deleteCallbacks && m_pCallbacks != &defaultCallbacks) {
delete m_pCallbacks;
}
}
/**
* @brief Register the extended advertisement data.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] adv The extended advertisement instance with the data to set.
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv) {
adv.m_params.sid = inst_id;
// Legacy advertising as connectable requires the scannable flag also.
if (adv.m_params.legacy_pdu && adv.m_params.connectable) {
adv.m_params.scannable = true;
}
// If connectable or not scannable disable the callback for scan response requests
if (adv.m_params.connectable || !adv.m_params.scannable) {
adv.m_params.scan_req_notif = false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
if (!pServer->m_gattsStarted) {
pServer->start();
}
}
int rc = ble_gap_ext_adv_configure(inst_id,
&adv.m_params,
NULL,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEExtAdvertising::handleGapEvent,
NULL);
#else
int rc = ble_gap_ext_adv_configure(inst_id,
&data.m_params,
NULL,
NimBLEExtAdvertising::handleGapEvent,
NULL);
#endif
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d", rc);
} else {
os_mbuf *buf;
buf = os_msys_get_pkthdr(adv.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d", rc);
return false;
} else {
if (adv.m_params.scannable && !adv.m_params.legacy_pdu) {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
} else {
rc = ble_gap_ext_adv_set_data(inst_id, buf);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc);
} else {
if (adv.m_advAddress != NimBLEAddress("")) {
ble_addr_t addr;
memcpy(&addr.val, adv.m_advAddress.getNative(), 6);
// Custom advertising address must be random.
addr.type = BLE_OWN_ADDR_RANDOM;
rc = ble_gap_ext_adv_set_addr(inst_id, &addr);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d", rc);
return false;
}
}
}
}
return (rc == 0);
}
/**
* @brief Set the scan response data for a legacy advertisement.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] lsr A reference to a NimBLEExtAdvertisement that contains the data.
*/
bool NimBLEExtAdvertising::setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & lsr) {
os_mbuf *buf = os_msys_get_pkthdr(lsr.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
int rc = os_mbuf_append(buf, &lsr.m_payload[0], lsr.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d", rc);
return false;
} else {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
}
return (rc == 0);
}
/**
* @brief Start extended advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::start(uint8_t inst_id, int duration, int max_events) {
NIMBLE_LOGD(LOG_TAG, ">> Extended Advertising start");
// If Host is not synced we cannot start advertising.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return false;
}
int rc = ble_gap_ext_adv_start(inst_id, duration / 10, max_events);
switch (rc) {
case 0:
m_advStatus[inst_id] = true;
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Value Error");
break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
NIMBLE_LOGD(LOG_TAG, "<< Extended Advertising start");
return (rc == 0 || rc == BLE_HS_EALREADY);
} // start
/**
* @brief Stop and remove this instance data from the advertisement set.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeInstance(uint8_t inst_id) {
if (stop(inst_id)) {
int rc = ble_gap_ext_adv_remove(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
return true;
}
return false;
} // removeInstance
/**
* @brief Stop and remove all advertising instance data.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeAll() {
if (stop()) {
int rc = ble_gap_ext_adv_clear();
if (rc == 0 || rc == BLE_HS_EALREADY) {
return true;
} else {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
}
return false;
} // removeAll
/**
* @brief Stop advertising this instance data.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop(uint8_t inst_id) {
int rc = ble_gap_ext_adv_stop(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
m_advStatus[inst_id] = false;
return true;
} // stop
/**
* @brief Stop all advertisements.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop() {
int rc = ble_gap_ext_adv_clear();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
for(auto it : m_advStatus) {
it = false;
}
return true;
} // stop
/**
* @brief Set a callback to call when the advertisement stops.
* @param [in] pCallbacks A pointer to a callback to be invoked when an advertisment stops.
* @param [in] deleteCallbacks if true callback class will be deleted when advertising is destructed.
*/
void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks,
bool deleteCallbacks) {
if (pCallbacks != nullptr){
m_pCallbacks = pCallbacks;
m_deleteCallbacks = deleteCallbacks;
} else {
m_pCallbacks = &defaultCallbacks;
}
} // setCallbacks
/**
* @brief Check if currently advertising.
* @param [in] inst_id The instance ID of the advertised data to get the status of.
* @return True if advertising is active.
*/
bool NimBLEExtAdvertising::isActive(uint8_t inst_id) {
return m_advStatus[inst_id];
} // isAdvertising
/**
* @brief Check if any instances are currently advertising.
* @return True if any instance is active.
*/
bool NimBLEExtAdvertising::isAdvertising() {
for (auto it : m_advStatus) {
if (it) {
return true;
}
}
return false;
} // isAdvertising
/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
void NimBLEExtAdvertising::onHostSync() {
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
for(auto it : m_advStatus) {
it = false;
}
} // onHostSync
/**
* @brief Handler for gap events when not using peripheral role.
* @param [in] event the event data.
* @param [in] arg pointer to the advertising instance.
*/
/*STATIC*/
int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
(void)arg;
NimBLEExtAdvertising* pAdv = NimBLEDevice::getAdvertising();
switch (event->type) {
case BLE_GAP_EVENT_ADV_COMPLETE: {
switch (event->adv_complete.reason) {
// Don't call the callback if host reset, we want to
// preserve the active flag until re-sync to restart advertising.
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
break;
}
pAdv->m_advStatus[event->adv_complete.instance] = false;
pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason,
event->adv_complete.instance);
break;
}
case BLE_GAP_EVENT_SCAN_REQ_RCVD: {
pAdv->m_pCallbacks->onScanRequest(pAdv, event->scan_req_rcvd.instance,
NimBLEAddress(event->scan_req_rcvd.scan_addr));
break;
}
}
return 0;
} // handleGapEvent
/** Default callback handlers */
void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising *pAdv,
int reason, uint8_t inst_id) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onStopped: Default");
} // onStopped
void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv,
uint8_t inst_id, NimBLEAddress addr) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onScanRequest: Default");
} // onScanRequest
/**
* @brief Construct a BLE extended advertisement.
* @param [in] priPhy The primary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
* @param [in] secPhy The secondary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy)
: m_advAddress("")
{
memset (&m_params, 0, sizeof(m_params));
m_params.own_addr_type = NimBLEDevice::m_own_addr_type;
m_params.primary_phy = priPhy;
m_params.secondary_phy = secPhy;
m_params.tx_power = 127;
} // NimBLEExtAdvertisement
/**
* @brief Sets wether the advertisement should use legacy (BLE 4.0, 31 bytes max) advertising.
* @param [in] val true = using legacy advertising.
*/
void NimBLEExtAdvertisement::setLegacyAdvertising(bool val) {
m_params.legacy_pdu = val;
} // setLegacyAdvertising
/**
* @brief Sets wether the advertisement has scan response data available.
* @param [in] val true = scan response is available.
*/
void NimBLEExtAdvertisement::setScannable(bool val) {
m_params.scannable = val;
} // setScannable
/**
* @brief Sets the transmission power level for this advertisement.
* @param [in] dbm the transmission power to use in dbm.
* @details The allowable value range depends on device hardware. \n
* The ESP32C3 and ESP32S3 have a range of -27 to +18.
*/
void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
m_params.tx_power = dbm;
}
/**
* @brief Sets wether this advertisement should advertise as a connectable device.
* @param [in] val True = connectable.
*/
void NimBLEExtAdvertisement::setConnectable(bool val) {
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_params.connectable = val;
#endif
} // setConnectable
/**
* @brief Set the address to use for this advertisement.
* @param [in] addr The address to use.
*/
void NimBLEExtAdvertisement::setAddress(const NimBLEAddress & addr) {
m_advAddress = addr;
// Must use random address type.
m_params.own_addr_type = BLE_OWN_ADDR_RANDOM;
}
/**
* @brief Sets The primary channels to advertise on.
* @param [in] ch37 Advertise on channel 37.
* @param [in] ch38 Advertise on channel 38.
* @param [in] ch39 Advertise on channel 39.
* @details This will set a bitmask using the input parameters to allow different \n
* combinations. If all inputs are false then all 3 channels will be used.
*/
void NimBLEExtAdvertisement::setPrimaryChannels(bool ch37, bool ch38, bool ch39) {
m_params.channel_map = (ch37 | (ch38 << 1) | (ch39 << 2));
} // setPrimaryChannels
/**
* @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 NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_NONE;
return;
}
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_SCAN;
return;
}
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_CONN;
return;
}
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_BOTH;
return;
}
} // setScanFilter
/**
* @brief Sets the peer to directly advertise to.
* @param [in] addr The address of the peer to direct the advertisements.
*/
void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, addr.getNative(), 6);
peerAddr.type = addr.getType();
m_params.peer = peerAddr;
} // setDirectedPeer
/**
* @brief Enable or disable direct advertisements to the peer set with `NimBLEExtAdvertisement::setDirectedPeer`
* @param [in] val true = send directed advertisements to peer.
* @param [in] high_duty true = use fast advertising rate, default - true.
*/
void NimBLEExtAdvertisement::setDirected(bool val, bool high_duty) {
m_params.directed = val;
m_params.high_duty_directed = high_duty;
} // setDirected
/**
* @brief Set the minimum advertising interval.
* @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMinInterval(uint32_t mininterval) {
m_params.itvl_min = mininterval;
} // setMinInterval
/**
* @brief Set the maximum advertising interval.
* @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMaxInterval(uint32_t maxinterval) {
m_params.itvl_max = maxinterval;
} // setMaxInterval
/**
* @brief Set the primary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setPrimaryPhy(uint8_t phy) {
m_params.primary_phy = phy;
} // setPrimaryPhy
/**
* @brief Set the secondary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setSecondaryPhy(uint8_t phy) {
m_params.secondary_phy = phy;
} // setSecondaryPhy
/**
* @brief Sets whether the advertisement should be anonymous.
* @param [in] val Set to true to enable anonymous advertising.
*
* @details Anonymous advertising omits the device's address from the advertisement.
*/
void NimBLEExtAdvertisement::setAnonymous(bool val) {
m_params.anonymous = val;
} // setAnonymous
/**
* @brief Sets whether the scan response request callback should be called.
* @param [in] enable If true the scan response request callback will be called for this advertisement.
*/
void NimBLEExtAdvertisement::enableScanRequestCallback(bool enable) {
m_params.scan_req_notif = enable;
} // enableScanRequestCallback
/**
* @brief Clears the data stored in this instance, does not change settings.
* @details This will clear all data but preserves advertising parameter settings.
*/
void NimBLEExtAdvertisement::clearData() {
std::vector<uint8_t> swap;
std::swap(m_payload, swap);
}
/**
* @brief Get the size of the current data.
*/
size_t NimBLEExtAdvertisement::getDataSize() {
return m_payload.size();
} // getDataSize
/**
* @brief Set the advertisement data.
* @param [in] data The data to be set as the payload.
* @param [in] length The size of data.
* @details This will completely replace any data that was previously set.
*/
void NimBLEExtAdvertisement::setData(const uint8_t * data, size_t length) {
m_payload.assign(data, data + length);
} // setData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
*/
void NimBLEExtAdvertisement::addData(const std::string &data) {
addData((uint8_t*)data.data(), data.length());
} // addData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload.
*/
void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
m_payload.insert(m_payload.end(), data, data + length);
} // 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 NimBLEExtAdvertisement::setAppearance(uint16_t appearance) {
char cdata[2];
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
} // setAppearance
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
*/
void NimBLEExtAdvertisement::setFlags(uint8_t flag) {
char cdata[3];
cdata[0] = 2;
cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01
cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
addData(std::string(cdata, 3));
} // setFlags
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEExtAdvertisement::setManufacturerData(const std::string &data) {
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
} // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
*/
void NimBLEExtAdvertisement::setURI(const std::string &uri) {
char cdata[2];
cdata[0] = uri.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_URI;
addData(std::string(cdata, 2) + uri);
} // setURI
/**
* @brief Set the complete name of this device.
* @param [in] name The name to advertise.
*/
void NimBLEExtAdvertisement::setName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
} // setName
/**
* @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices(const NimBLEUUID &uuid) {
setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices
/**
* @brief Set the complete list of 16 bit services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 16, v_uuid);
} // setCompleteServices16
/**
* @brief Set the complete list of 32 bit services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 32, v_uuid);
} // setCompleteServices32
/**
* @brief Set a single service to advertise as a partial list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices(const NimBLEUUID &uuid) {
setServices(false, uuid.bitSize(), {uuid});
} // setPartialServices
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 16, v_uuid);
} // setPartialServices16
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 32, v_uuid);
} // setPartialServices32
/**
* @brief Utility function to create the list of service UUID's from a vector.
* @param [in] complete If true the vector is the complete set of services.
* @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
* @param [in] v_uuid The vector of service UUID's to advertise.
*/
void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid)
{
char cdata[2];
cdata[0] = (size / 8) * v_uuid.size() + 1;
switch(size) {
case 16:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
break;
case 32:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
default:
return;
}
std::string uuids;
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
}
}
addData(std::string(cdata, 2) + uuids);
} // setServices
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data.
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
break;
}
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
break;
}
case 128: {
// [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data);
break;
}
default:
return;
}
} // setServiceData
/**
* @brief Set the short name.
* @param [in] name The short name of the device.
*/
void NimBLEExtAdvertisement::setShortName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
} // setShortName
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEExtAdvertisement::addTxPower() {
m_params.include_tx_power = 1;
} // addTxPower
/**
* @brief Set the preferred connection interval parameters.
* @param [in] min The minimum interval desired.
* @param [in] max The maximum interval desired.
*/
void NimBLEExtAdvertisement::setPreferredParams(uint16_t min, uint16_t max) {
uint8_t data[6];
data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
data[2] = min;
data[3] = min >> 8;
data[4] = max;
data[5] = max >> 8;
addData(data, 6);
} // setPreferredParams
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */

View File

@@ -1,152 +0,0 @@
/*
* NimBLEExtAdvertising.h
*
* Created: on February 6, 2022
* Author H2zero
*/
#ifndef MAIN_BLEEXTADVERTISING_H_
#define MAIN_BLEEXTADVERTISING_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include <vector>
class NimBLEExtAdvertisingCallbacks;
/**
* @brief Extended advertisement data
*/
class NimBLEExtAdvertisement {
public:
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M,
uint8_t secPhy = BLE_HCI_LE_PHY_1M);
void setAppearance(uint16_t appearance);
void setCompleteServices(const NimBLEUUID &uuid);
void setCompleteServices16(const std::vector<NimBLEUUID> &v_uuid);
void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid);
void setFlags(uint8_t flag);
void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid);
void setPartialServices16(const std::vector<NimBLEUUID> &v_uuid);
void setPartialServices32(const std::vector<NimBLEUUID> &v_uuid);
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name);
void setData(const uint8_t * data, size_t length);
void addData(const std::string &data);
void addData(const uint8_t * data, size_t length);
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
void setLegacyAdvertising(bool val);
void setConnectable(bool val);
void setScannable(bool val);
void setMinInterval(uint32_t mininterval);
void setMaxInterval(uint32_t maxinterval);
void setPrimaryPhy(uint8_t phy);
void setSecondaryPhy(uint8_t phy);
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setDirectedPeer(const NimBLEAddress & addr);
void setDirected(bool val, bool high_duty = true);
void setAnonymous(bool val);
void setPrimaryChannels(bool ch37, bool ch38, bool ch39);
void setTxPower(int8_t dbm);
void setAddress(const NimBLEAddress & addr);
void enableScanRequestCallback(bool enable);
void clearData();
size_t getDataSize();
private:
friend class NimBLEExtAdvertising;
void setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid);
std::vector<uint8_t> m_payload;
ble_gap_ext_adv_params m_params;
NimBLEAddress m_advAddress;
}; // NimBLEExtAdvertisement
/**
* @brief Extended advertising class.
*/
class NimBLEExtAdvertising {
public:
/**
* @brief Construct an extended advertising object.
*/
NimBLEExtAdvertising() :m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {}
~NimBLEExtAdvertising();
bool start(uint8_t inst_id, int duration = 0, int max_events = 0);
bool setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv);
bool setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & data);
bool removeInstance(uint8_t inst_id);
bool removeAll();
bool stop(uint8_t inst_id);
bool stop();
bool isActive(uint8_t inst_id);
bool isAdvertising();
void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks,
bool deleteCallbacks = true);
private:
friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
bool m_scanResp;
bool m_deleteCallbacks;
NimBLEExtAdvertisingCallbacks* m_pCallbacks;
ble_gap_ext_adv_params m_advParams;
std::vector<bool> m_advStatus;
};
/**
* @brief Callbacks associated with NimBLEExtAdvertising class.
*/
class NimBLEExtAdvertisingCallbacks {
public:
virtual ~NimBLEExtAdvertisingCallbacks() {};
/**
* @brief Handle an advertising stop event.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] reason The reason code for stopping the advertising.
* @param [in] inst_id The instance ID of the advertisement that was stopped.
*/
virtual void onStopped(NimBLEExtAdvertising *pAdv, int reason, uint8_t inst_id);
/**
* @brief Handle a scan response request.
* This is called when a scanning device requests a scan response.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] inst_id The instance ID of the advertisement that the scan response request was made.
* @param [in] addr The address of the device making the request.
*/
virtual void onScanRequest(NimBLEExtAdvertising *pAdv, uint8_t inst_id, NimBLEAddress addr);
}; // NimBLEExtAdvertisingCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* MAIN_BLEADVERTISING_H_ */

View File

@@ -1,35 +0,0 @@
/*
* NimBLEMeshCreateModel.cpp
*
* Created: on April 27 2022
* Author H2zero
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshCreateModel.h"
static struct bt_mesh_model_cb mod_cb = {
//.init = modelInitCallback
};
struct bt_mesh_model createConfigSrvModel(struct bt_mesh_cfg_srv* cfg) {
struct bt_mesh_model cmod = BT_MESH_MODEL_CFG_SRV(cfg);
return cmod;
}
struct bt_mesh_model createHealthModel(struct bt_mesh_health_srv* hsrv,
struct bt_mesh_model_pub* hpub) {
struct bt_mesh_model hmod = BT_MESH_MODEL_HEALTH_SRV(hsrv, hpub);
return hmod;
}
struct bt_mesh_model createGenModel(int16_t _id, struct bt_mesh_model_op* op,
struct bt_mesh_model_pub* pub, void* udata) {
struct bt_mesh_model mod = BT_MESH_MODEL_CB(_id, op, pub, udata, &mod_cb);
return mod;
}
#endif // CONFIG_BT_NIMBLE_MESH

View File

@@ -1,35 +0,0 @@
/*
* NimBLEMeshCreateModel.h
*
* Created: on April 27 2022
* Author H2zero
*
*/
#ifndef __NIMBLE_MESH_CREATE_MODEL_H
#define __NIMBLE_MESH_CREATE_MODEL_H
#include "nimconfig.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "mesh/mesh.h"
# include "mesh/cfg_srv.h"
#else
# include "nimble/nimble/host/mesh/include/mesh/mesh.h"
# include "nimble/nimble/host/mesh/include/mesh/cfg_srv.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
//int modelInitCallback(struct bt_mesh_model *model);
struct bt_mesh_model createConfigSrvModel(struct bt_mesh_cfg_srv* cfg);
struct bt_mesh_model createHealthModel(struct bt_mesh_health_srv* hsrv,
struct bt_mesh_model_pub* hpub);
struct bt_mesh_model createGenModel(int16_t _id, struct bt_mesh_model_op* op,
struct bt_mesh_model_pub* pub, void* udata);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,145 +0,0 @@
/*
* NimBLEMeshElement.cpp
*
* Created: on Aug 23 2020
* Author H2zero
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshElement.h"
#include "NimBLELog.h"
#include "NimBLEMeshCreateModel.h"
static const char* LOG_TAG = "NimBLEMeshElement";
NimBLEMeshElement::NimBLEMeshElement() {
m_pElem = nullptr;
m_pHealthModel = nullptr;
}
NimBLEMeshElement::~NimBLEMeshElement() {
if(m_pElem != nullptr) {
delete m_pElem;
}
if(m_pHealthModel != nullptr) {
delete m_pHealthModel;
}
for(auto &it : m_modelsVec) {
if(it.id != BT_MESH_MODEL_ID_HEALTH_SRV) {
delete (NimBLEMeshModel*)it.user_data;
}
}
m_modelsVec.clear();
}
/**
* @brief Creates a model and adds it the the elements model vector.
* @param [in] type The type of model to create.
* @param [in] pCallbacks a pointer to a callback instance for this model.
*/
NimBLEMeshModel* NimBLEMeshElement::createModel(uint16_t type, NimBLEMeshModelCallbacks *pCallbacks) {
if(getModel(type) != nullptr) {
NIMBLE_LOGE(LOG_TAG, "Error: element already has a type %04x model", type);
return nullptr;
}
NIMBLE_LOGD(LOG_TAG, "Creating model type: %04x", type);
NimBLEMeshModel* pModel = nullptr;
switch(type)
{
case BT_MESH_MODEL_ID_GEN_ONOFF_SRV:
pModel = new NimBLEGenOnOffSrvModel(pCallbacks);
break;
case BT_MESH_MODEL_ID_GEN_LEVEL_SRV:
pModel = new NimBLEGenLevelSrvModel(pCallbacks);
break;
case BT_MESH_MODEL_ID_HEALTH_SRV:
m_pHealthModel = new NimBLEHealthSrvModel(pCallbacks);
pModel = m_pHealthModel;
m_modelsVec.push_back(createHealthModel(&m_pHealthModel->m_healthSrv, &pModel->m_opPub));
return pModel;
default:
NIMBLE_LOGE(LOG_TAG, "Error: model type %04x not supported", type);
return nullptr;
}
m_modelsVec.push_back(createGenModel(type, pModel->m_opList, &pModel->m_opPub, pModel));
return pModel;
}
/**
* @brief Adds a model created outside of element context to the elements model vector.
* @param [in] model A pointer to the model instance to add.
*/
void NimBLEMeshElement::addModel(const bt_mesh_model & model) {
m_modelsVec.push_back(model);
}
/**
* @brief Get a pointer to the model in the element with the type specified.
* @param [in] The model type requested.
* @returns A pointer to the model or nullptr if not found.
*/
NimBLEMeshModel* NimBLEMeshElement::getModel(uint16_t type) {
if(type == BT_MESH_MODEL_ID_HEALTH_SRV) {
return m_pHealthModel;
}
for(auto &it : m_modelsVec) {
if(it.id == type) {
return (NimBLEMeshModel*)it.user_data;
}
}
return nullptr;
}
/**
* @brief Get a pointer to a model with matching type and ID.
* @param [in] eidx The element ID to compare.
* @param [in] midx The model ID to compare.
* @param [in] The model type requested.
* @returns A pointer to the model or nullptr if not found.
*/
NimBLEMeshModel* NimBLEMeshElement::getModelByIdx(uint8_t eidx, uint8_t midx, uint16_t type) {
for(auto &it : m_modelsVec) {
if(it.elem_idx == eidx && it.mod_idx == midx) {
if(type == BT_MESH_MODEL_ID_HEALTH_SRV) {
return m_pHealthModel;
} else {
return (NimBLEMeshModel*)it.user_data;
}
}
}
return nullptr;
}
/**
* @brief Creates a bt_mesh_elem for registering with the nimble stack.
* @returns A pointer to the bt_mesh_elem created.
* @details Must not be called until all models have been added.
*/
bt_mesh_elem* NimBLEMeshElement::start() {
m_pElem = new bt_mesh_elem{0, 0, uint8_t(m_modelsVec.size()), 0, &m_modelsVec[0], NULL};
return m_pElem;
}
#endif // CONFIG_BT_NIMBLE_MESH

View File

@@ -1,46 +0,0 @@
/*
* NimBLEMeshElement.h
*
* Created: on Aug 23 2020
* Author H2zero
*
*/
#ifndef MAIN_NIMBLE_MESH_ELEMENT_H_
#define MAIN_NIMBLE_MESH_ELEMENT_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#include "NimBLEMeshNode.h"
#include "NimBLEMeshModel.h"
#include <vector>
class NimBLEMeshModelCallbacks;
class NimBLEMeshModel;
class NimBLEHealthSrvModel;
class NimBLEMeshElement {
public:
NimBLEMeshModel* createModel(uint16_t type, NimBLEMeshModelCallbacks* pCallbacks=nullptr);
NimBLEMeshModel* getModel(uint16_t type);
NimBLEMeshModel* getModelByIdx(uint8_t eidx, uint8_t midx, uint16_t type);
private:
friend class NimBLEMeshNode;
NimBLEMeshElement();
~NimBLEMeshElement();
void addModel(const bt_mesh_model & model);
bt_mesh_elem* start();
bt_mesh_elem *m_pElem;
NimBLEHealthSrvModel* m_pHealthModel;
std::vector<bt_mesh_model> m_modelsVec;
};
#endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLE_MESH_ELEMENT_H_

View File

@@ -1,700 +0,0 @@
/*
* NimBLEMeshModel.cpp
*
* Created: on Aug 25 2020
* Author H2zero
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshModel.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/nimble_port.h"
#else
# include "nimble/porting/nimble/include/nimble/nimble_port.h"
#endif
#include "NimBLEDevice.h"
#define CID_VENDOR 0x05C3
#define STANDARD_TEST_ID 0x00
static const char* LOG_TAG = "NimBLEMeshModel";
static NimBLEMeshModelCallbacks defaultCallbacks;
static const struct bt_mesh_health_srv_cb health_srv_cb = {
NimBLEHealthSrvCallbacks::faultGetCurrent,
NimBLEHealthSrvCallbacks::faultGetRegistered,
NimBLEHealthSrvCallbacks::faultClear,
NimBLEHealthSrvCallbacks::faultTest,
NimBLEHealthSrvCallbacks::attentionOn,
NimBLEHealthSrvCallbacks::attentionOff
};
/**
* @brief base model constructor
* @param [in] pCallbacks, a pointer to a callback instance for model operations
*/
NimBLEMeshModel::NimBLEMeshModel(NimBLEMeshModelCallbacks *pCallbacks,
uint16_t initDataSize, uint16_t maxDataSize)
: m_value(initDataSize, maxDataSize),
m_targetValue(initDataSize, maxDataSize)
{
if(pCallbacks == nullptr) {
m_callbacks = &defaultCallbacks;
} else {
m_callbacks = pCallbacks;
}
m_opList = nullptr;
m_lastTid = 0;
m_lastSrcAddr = 0;
m_lastDstAddr = 0;
m_lastMsgTime = 0;
m_transTime = 0;
m_delayTime = 0;
m_transStep = 0;
memset(&m_opPub, 0, sizeof(m_opPub));
memset(&m_tdTimer, 0, sizeof(m_tdTimer));
memset(&m_pubTimer, 0, sizeof(m_pubTimer));
}
/**
* @brief destructor
*/
NimBLEMeshModel::~NimBLEMeshModel() {
if(m_opList != nullptr) {
delete[] m_opList;
}
}
int NimBLEMeshModel::extractTransTimeDelay(os_mbuf *buf)
{
switch(buf->om_len) {
case 0x00:
m_transTime = 0;
m_delayTime = 0;
return 0;
case 0x02:
m_transTime = buf->om_data[0];
if((m_transTime & 0x3F) == 0x3F) {
// unknown transition time
m_transTime = 0;
m_delayTime = 0;
return BLE_HS_EINVAL;
}
m_delayTime = buf->om_data[1];
return 0;
default:
return BLE_HS_EMSGSIZE;
}
}
bool NimBLEMeshModel::checkRetransmit(uint8_t tid, bt_mesh_msg_ctx *ctx) {
time_t now = time(nullptr);
if(m_lastTid == tid &&
m_lastSrcAddr == ctx->addr &&
m_lastDstAddr == ctx->recv_dst &&
(now - m_lastMsgTime <= 6)) {
NIMBLE_LOGD(LOG_TAG, "Ignoring retransmit");
return true;
}
m_lastTid = tid;
m_lastSrcAddr = ctx->addr;
m_lastDstAddr = ctx->recv_dst;
m_lastMsgTime = now;
return false;
}
void NimBLEMeshModel::sendMessage(bt_mesh_model *model, bt_mesh_msg_ctx *ctx, os_mbuf *msg) {
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
NIMBLE_LOGE(LOG_TAG, "Send status failed");
}
os_mbuf_free_chain(msg);
}
void NimBLEMeshModel::startTdTimer(ble_npl_time_t timerMs) {
ble_npl_time_t ticks;
ble_npl_time_ms_to_ticks(timerMs, &ticks);
ble_npl_callout_reset(&m_tdTimer, ticks);
}
void NimBLEMeshModel::publish() {
ble_npl_callout_reset(&m_pubTimer, 1);
}
uint32_t NimBLEMeshModel::getTransTime() {
return (m_transTime & 0x3F) * NimBLEUtils::meshTransTimeMs(m_transTime);
}
uint16_t NimBLEMeshModel::getDelayTime() {
return m_delayTime * 5;
}
/**
* @brief Generic on/off server model constructor
* @param [in] pCallbacks, a pointer to a callback instance for model operations
*/
NimBLEGenOnOffSrvModel::NimBLEGenOnOffSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
:NimBLEMeshModel(pCallbacks, 1, 1)
{
// Register the opcodes for this model with the required callbacks
m_opList = new bt_mesh_model_op[4]{
{ BT_MESH_MODEL_OP_2(0x82, 0x01), 0, NimBLEGenOnOffSrvModel::getOnOff },
{ BT_MESH_MODEL_OP_2(0x82, 0x02), 2, NimBLEGenOnOffSrvModel::setOnOff },
{ BT_MESH_MODEL_OP_2(0x82, 0x03), 2, NimBLEGenOnOffSrvModel::setOnOffUnack },
BT_MESH_MODEL_OP_END};
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
NimBLEGenOnOffSrvModel::tdTimerCb, this);
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
NimBLEGenOnOffSrvModel::pubTimerCb, this);
m_opPub.msg = NET_BUF_SIMPLE(2 + 3);
}
/**
* @brief Called by the NimBLE stack to get the on/off status of the model
*/
void NimBLEGenOnOffSrvModel::getOnOff(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
if(pModel->m_callbacks != &defaultCallbacks) {
pModel->setValue(pModel->m_callbacks->getOnOff(pModel));
}
pModel->setPubMsg();
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
NIMBLE_LOGE(LOG_TAG, "Send status failed");
}
}
/**
* @brief Called by the NimBLE stack to set the status of the model with acknowledgement.
*/
void NimBLEGenOnOffSrvModel::setOnOff(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
// Rather than duplicate code just call the unack function then send the status
NimBLEGenOnOffSrvModel::setOnOffUnack(model,ctx,buf);
NimBLEGenOnOffSrvModel::getOnOff(model,ctx,buf);
}
/**
* @brief Called by the NimBLE stack to set the status of the model without acknowledgement.
*/
void NimBLEGenOnOffSrvModel::setOnOffUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
uint8_t newval = net_buf_simple_pull_u8(buf);
uint8_t tid = net_buf_simple_pull_u8(buf);
if(pModel->checkRetransmit(tid, ctx)) {
return;
}
if(pModel->extractTransTimeDelay(buf) != 0) {
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
return;
}
// stop the transition timer to handle the new input
ble_npl_callout_stop(&pModel->m_tdTimer);
// Mesh spec says transition to "ON state" happens immediately
// after delay, so ignore the transition time.
if(newval == 1) {
pModel->m_transTime = 0;
}
ble_npl_time_t timerMs = 0;
if(newval != pModel->m_value[0]) {
pModel->m_targetValue.setValue(&newval, sizeof(newval));
if(pModel->m_delayTime > 0) {
timerMs = 5 * pModel->m_delayTime;
} else if(pModel->m_transTime & 0x3F) {
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
pModel->m_transTime -= 1;
}
}
if(timerMs > 0) {
pModel->startTdTimer(timerMs);
} else {
pModel->m_value = pModel->m_targetValue;
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
}
}
void NimBLEGenOnOffSrvModel::tdTimerCb(ble_npl_event *event) {
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
if(pModel->m_delayTime > 0) {
pModel->m_delayTime = 0;
}
if((pModel->m_transTime & 0x3F) && pModel->m_targetValue[0] == 0) {
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
pModel->m_transTime -= 1;
pModel->publish();
return;
}
pModel->m_transTime = 0;
pModel->m_value = pModel->m_targetValue;
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
}
void NimBLEGenOnOffSrvModel::pubTimerCb(ble_npl_event *event) {
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
pModel->setPubMsg();
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
if(err != 0) {
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
}
}
void NimBLEGenOnOffSrvModel::setPubMsg() {
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
net_buf_simple_add_u8(m_opPub.msg, m_value[0]);
if(m_transTime > 0) {
net_buf_simple_add_u8(m_opPub.msg, m_targetValue[0]);
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
m_transTime : m_transTime + 1);
}
}
void NimBLEGenOnOffSrvModel::setValue(uint8_t *val, size_t len) {
if(len != sizeof(uint8_t)) {
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect value length");
return;
}
m_value.setValue(val, len);
}
void NimBLEGenOnOffSrvModel::setTargetValue(uint8_t *val, size_t len) {
if(len != sizeof(uint8_t)) {
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect target value length");
return;
}
m_targetValue.setValue(val, len);
}
/**
* @brief Generic level server model constructor
* @param [in] pCallbacks, a pointer to a callback instance for model operations
*/
NimBLEGenLevelSrvModel::NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
:NimBLEMeshModel(pCallbacks, 2, 2)
{
// Register the opcodes for this model with the required callbacks
m_opList = new bt_mesh_model_op[8]{
{ BT_MESH_MODEL_OP_2(0x82, 0x05), 0, NimBLEGenLevelSrvModel::getLevel },
{ BT_MESH_MODEL_OP_2(0x82, 0x06), 3, NimBLEGenLevelSrvModel::setLevel },
{ BT_MESH_MODEL_OP_2(0x82, 0x07), 3, NimBLEGenLevelSrvModel::setLevelUnack },
{ BT_MESH_MODEL_OP_2(0x82, 0x09), 5, NimBLEGenLevelSrvModel::setDelta },
{ BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, NimBLEGenLevelSrvModel::setDeltaUnack },
{ BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, NimBLEGenLevelSrvModel::setMove },
{ BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, NimBLEGenLevelSrvModel::setMoveUnack },
BT_MESH_MODEL_OP_END};
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
NimBLEGenLevelSrvModel::tdTimerCb, this);
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
NimBLEGenLevelSrvModel::pubTimerCb, this);
m_opPub.msg = NET_BUF_SIMPLE(2 + 5);
}
/**
* @brief Called by the NimBLE stack to get the level value of the model.
*/
void NimBLEGenLevelSrvModel::getLevel(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
if(pModel->m_callbacks != &defaultCallbacks) {
pModel->setValue(pModel->m_callbacks->getLevel(pModel));
}
pModel->setPubMsg();
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
NIMBLE_LOGE(LOG_TAG, "Send status failed");
}
}
/**
* @brief Called by the NimBLE stack to set the level value of the model.
*/
void NimBLEGenLevelSrvModel::setLevel(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, buf);
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
}
/**
* @brief Called by the NimBLE stack to set the level value of the model without acknowledgement.
*/
void NimBLEGenLevelSrvModel::setLevelUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
int16_t newval = (int16_t) net_buf_simple_pull_le16(buf);
uint8_t tid = net_buf_simple_pull_u8(buf);
if(pModel->checkRetransmit(tid, ctx)) {
return;
}
if(pModel->extractTransTimeDelay(buf) != 0) {
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
return;
}
// stop the transition timer to handle the new input
ble_npl_callout_stop(&pModel->m_tdTimer);
ble_npl_time_t timerMs = 0;
int16_t curval = pModel->m_value.getValue<int16_t>(nullptr, true);
if(newval != curval) {
pModel->m_targetValue.setValue(newval);
if(pModel->m_delayTime > 0) {
timerMs = 5 * pModel->m_delayTime;
}
if(pModel->m_transTime & 0x3F) {
pModel->m_transStep = -1 * ((curval - newval) / (pModel->m_transTime & 0x3F));
if(timerMs == 0) {
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
pModel->m_transTime -= 1;
}
}
}
if(timerMs > 0) {
pModel->startTdTimer(timerMs);
} else {
pModel->m_value = pModel->m_targetValue;
pModel->m_callbacks->setLevel(pModel, newval);
}
}
/**
* @brief Called by the NimBLE stack to set the level value by delta of the model.
*/
void NimBLEGenLevelSrvModel::setDelta(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, buf);
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
}
/**
* @brief Called by the NimBLE stack to set the level value by delta without acknowledgement.
*/
void NimBLEGenLevelSrvModel::setDeltaUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
int32_t delta = (int32_t) net_buf_simple_pull_le32(buf);
int32_t temp32 = pModel->m_value.getValue<int16_t>(nullptr, true) + delta;
if (temp32 < INT16_MIN) {
temp32 = INT16_MIN;
} else if (temp32 > INT16_MAX) {
temp32 = INT16_MAX;
}
net_buf_simple_push_le16(buf, (uint16_t)temp32);
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, buf);
}
void NimBLEGenLevelSrvModel::setMove(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
NimBLEGenLevelSrvModel::setMoveUnack(model, ctx, buf);
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
}
void NimBLEGenLevelSrvModel::setMoveUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf)
{
int16_t delta = (int16_t) net_buf_simple_pull_le16(buf);
// Check if a transition time is present, if not then ignore this message.
// See: bluetooth mesh specifcation
if(buf->om_len < 3) {
return;
}
put_le32(net_buf_simple_push(buf, 4), (int32_t)delta);
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, buf);
}
void NimBLEGenLevelSrvModel::tdTimerCb(ble_npl_event *event) {
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
if(pModel->m_delayTime > 0) {
pModel->m_delayTime = 0;
}
if(pModel->m_transTime & 0x3F) {
int16_t newval = pModel->m_value.getValue<int16_t>(nullptr, true) + pModel->m_transStep;
pModel->m_value.setValue(newval);
pModel->m_callbacks->setLevel(pModel, newval);
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
pModel->m_transTime -= 1;
return;
}
pModel->m_transTime = 0;
pModel->m_value = pModel->m_targetValue;
pModel->m_callbacks->setLevel(pModel, pModel->m_value.getValue<int16_t>(nullptr, true));
}
void NimBLEGenLevelSrvModel::pubTimerCb(ble_npl_event *event) {
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
pModel->setPubMsg();
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
if(err != 0) {
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
}
}
void NimBLEGenLevelSrvModel::setPubMsg() {
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
net_buf_simple_add_le16(m_opPub.msg, m_value.getValue<int16_t>(nullptr, true));
if(m_transTime > 0) {
net_buf_simple_add_le16(m_opPub.msg, m_targetValue.getValue<int16_t>(nullptr, true));
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
m_transTime : m_transTime + 1);
}
}
void NimBLEGenLevelSrvModel::setValue(uint8_t *val, size_t len) {
if(len != sizeof(int16_t)) {
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect value length");
return;
}
m_value.setValue(val, len);
}
void NimBLEGenLevelSrvModel::setTargetValue(uint8_t *val, size_t len) {
if(len != sizeof(int16_t)) {
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect target value length");
return;
}
m_targetValue.setValue(val, len);
}
/**
* @brief Health server model constructor
* @param [in] pCallbacks, a pointer to a callback instance for model operations
*/
NimBLEHealthSrvModel::NimBLEHealthSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
:NimBLEMeshModel(pCallbacks, 1, 1)
{
memset(&m_healthSrv, 0, sizeof(m_healthSrv));
m_healthSrv.cb = &health_srv_cb;
m_opPub.msg = NET_BUF_SIMPLE(1 + 3);
m_hasFault = false;
m_testId = 0;
}
void NimBLEHealthSrvModel::setFault(uint8_t fault) {
m_faults.push_back(fault);
m_hasFault = true;
}
void NimBLEHealthSrvModel::clearFaults() {
m_faults.clear();
m_hasFault = false;
}
/**
* Default model callbacks
*/
NimBLEMeshModelCallbacks::~NimBLEMeshModelCallbacks() {}
void NimBLEMeshModelCallbacks::setOnOff(NimBLEMeshModel *pModel, uint8_t val) {
NIMBLE_LOGD(LOG_TAG, "Gen On/Off set val: %d", val);
}
uint8_t NimBLEMeshModelCallbacks::getOnOff(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Gen On/Off get");
return 0;
}
void NimBLEMeshModelCallbacks::setLevel(NimBLEMeshModel *pModel, int16_t val) {
NIMBLE_LOGD(LOG_TAG, "Gen Level set val: %d", val);
}
int16_t NimBLEMeshModelCallbacks::getLevel(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Gen Level get");
return 0;
}
void NimBLEMeshModelCallbacks::attentionOn(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Attention On Default");
}
void NimBLEMeshModelCallbacks::attentionOff(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Attention Off Default");
}
void NimBLEMeshModelCallbacks::faultTest(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Fault Test");
}
void NimBLEMeshModelCallbacks::faultClear(NimBLEMeshModel *pModel) {
NIMBLE_LOGD(LOG_TAG, "Fault Clear");
}
/**
* @brief Health server callbacks
*/
int NimBLEHealthSrvCallbacks::faultGetCurrent(bt_mesh_model *model, uint8_t *test_id,
uint16_t *company_id, uint8_t *faults,
uint8_t *fault_count)
{
NIMBLE_LOGD(LOG_TAG, "faultGetCurrent");
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
*test_id = pModel->m_testId;
*company_id = CID_VENDOR;
*fault_count = std::min(*(size_t*)fault_count, pModel->m_faults.size());
memcpy(faults, &pModel->m_faults[0], *fault_count);
return 0;
}
int NimBLEHealthSrvCallbacks::faultGetRegistered(bt_mesh_model *model, uint16_t company_id,
uint8_t *test_id, uint8_t *faults,
uint8_t *fault_count)
{
NIMBLE_LOGD(LOG_TAG, "faultGetRegistered");
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
*test_id = pModel->m_testId;
*fault_count = std::min(*(size_t*)fault_count, pModel->m_faults.size());
memcpy(faults, &pModel->m_faults[0], *fault_count);
return 0;
}
int NimBLEHealthSrvCallbacks::faultClear(bt_mesh_model *model, uint16_t company_id)
{
NIMBLE_LOGD(LOG_TAG, "faultClear - default");
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
pModel->m_callbacks->faultClear(pModel);
pModel->clearFaults();
return 0;
}
int NimBLEHealthSrvCallbacks::faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
{
NIMBLE_LOGD(LOG_TAG, "faultTest - default");
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
if (test_id != STANDARD_TEST_ID) {
return -BLE_HS_EINVAL;
}
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
pModel->setFault(0);
pModel->m_testId = test_id;
pModel->m_callbacks->faultTest(pModel);
return 0;
}
void NimBLEHealthSrvCallbacks::attentionOn(bt_mesh_model *model)
{
NIMBLE_LOGD(LOG_TAG, "attentionOn - default");
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
pModel->m_callbacks->attentionOn(pModel);
}
void NimBLEHealthSrvCallbacks::attentionOff(bt_mesh_model *model)
{
NIMBLE_LOGD(LOG_TAG, "attentionOff - default");
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
pModel->m_callbacks->attentionOff(pModel);
}
#endif // CONFIG_BT_NIMBLE_MESH

View File

@@ -1,197 +0,0 @@
/*
* NimBLEMeshModel.h
*
* Created: on Aug 25 2020
* Author H2zero
*
*/
#ifndef MAIN_NIMBLE_MESH_MODEL_H_
#define MAIN_NIMBLE_MESH_MODEL_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#include "NimBLEMeshElement.h"
#include "NimBLEAttValue.h"
#include <vector>
class NimBLEMeshModelCallbacks;
class NimBLEMeshModel {
public:
NimBLEMeshModel(NimBLEMeshModelCallbacks* pCallbacks,
uint16_t initDataSize = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH,
uint16_t maxDataSize = BLE_ATT_ATTR_MAX_LEN);
virtual ~NimBLEMeshModel();
int extractTransTimeDelay(os_mbuf *buf);
bool checkRetransmit(uint8_t tid, bt_mesh_msg_ctx *ctx);
void sendMessage(bt_mesh_model *model, bt_mesh_msg_ctx *ctx, os_mbuf *msg);
void startTdTimer(ble_npl_time_t timerMs);
void publish();
uint32_t getTransTime();
uint16_t getDelayTime();
virtual void setPubMsg(){};
virtual void setValue(uint8_t *val, size_t len){};
virtual void setTargetValue(uint8_t *val, size_t len){};
virtual void setFault(uint8_t){};
virtual void clearFaults(){};
template<typename T>
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
template<typename T>
void setTargetValue(const T &s) {
setTargetValue((uint8_t*)&s, sizeof(T));
}
template<typename T>
void getValue(T &s) {
s = (T)m_value[0];
}
template<typename T>
void getTargetValue(T &s) {
s = (T)m_targetValue[0];
}
bt_mesh_model_op* m_opList;
bt_mesh_model_pub m_opPub;
NimBLEMeshModelCallbacks* m_callbacks;
uint8_t m_lastTid;
uint16_t m_lastSrcAddr;
uint16_t m_lastDstAddr;
time_t m_lastMsgTime;
uint8_t m_transTime;
uint8_t m_delayTime;
NimBLEAttValue m_value;
NimBLEAttValue m_targetValue;
int16_t m_transStep;
ble_npl_callout m_tdTimer;
ble_npl_callout m_pubTimer;
};
class NimBLEGenOnOffSrvModel : NimBLEMeshModel {
friend class NimBLEMeshElement;
friend class NimBLEMeshNode;
NimBLEGenOnOffSrvModel(NimBLEMeshModelCallbacks *pCallbacks);
~NimBLEGenOnOffSrvModel(){};
static void getOnOff(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setOnOff(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setOnOffUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void tdTimerCb(ble_npl_event *event);
static void pubTimerCb(ble_npl_event *event);
void setPubMsg() override;
void setValue(uint8_t *val, size_t len) override;
void setTargetValue(uint8_t *val, size_t len) override;
};
class NimBLEGenLevelSrvModel : NimBLEMeshModel {
friend class NimBLEMeshElement;
friend class NimBLEMeshNode;
NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks *pCallbacks);
~NimBLEGenLevelSrvModel(){};
static void getLevel(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setLevel(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setLevelUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setDelta(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setDeltaUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setMove(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void setMoveUnack(bt_mesh_model *model,
bt_mesh_msg_ctx *ctx,
os_mbuf *buf);
static void tdTimerCb(ble_npl_event *event);
static void pubTimerCb(ble_npl_event *event);
void setPubMsg() override;
void setValue(uint8_t *val, size_t len) override;
void setTargetValue(uint8_t *val, size_t len) override;
};
class NimBLEHealthSrvModel : NimBLEMeshModel {
friend class NimBLEMeshElement;
friend class NimBLEMeshNode;
friend class NimBLEHealthSrvCallbacks;
NimBLEHealthSrvModel(NimBLEMeshModelCallbacks *pCallbacks);
~NimBLEHealthSrvModel(){};
public:
void setFault(uint8_t) override;
void clearFaults() override;
private:
bt_mesh_health_srv m_healthSrv;
bool m_hasFault;
uint8_t m_testId;
std::vector<uint8_t> m_faults;
};
class NimBLEMeshModelCallbacks {
public:
virtual ~NimBLEMeshModelCallbacks();
virtual void setOnOff(NimBLEMeshModel *pModel, uint8_t val);
virtual uint8_t getOnOff(NimBLEMeshModel *pModel);
virtual void setLevel(NimBLEMeshModel *pModel, int16_t val);
virtual int16_t getLevel(NimBLEMeshModel *pModel);
virtual void attentionOn(NimBLEMeshModel *pModel);
virtual void attentionOff(NimBLEMeshModel *pModel);
virtual void faultTest(NimBLEMeshModel *pModel);
virtual void faultClear(NimBLEMeshModel *pModel);
};
class NimBLEHealthSrvCallbacks {
public:
static int faultGetCurrent(bt_mesh_model *model, uint8_t *test_id,
uint16_t *company_id, uint8_t *faults,
uint8_t *fault_count);
static int faultGetRegistered(bt_mesh_model *model, uint16_t company_id,
uint8_t *test_id, uint8_t *faults,
uint8_t *fault_count);
static int faultClear(bt_mesh_model *model, uint16_t company_id);
static int faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id);
static void attentionOn(bt_mesh_model *model);
static void attentionOff(bt_mesh_model *model);
};
#endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLE_MESH_MODEL_H_

View File

@@ -1,209 +0,0 @@
/*
* NimBLEMeshNode.cpp
*
* Created: on July 22 2020
* Author H2zero
*
*/
#include "nimconfig.h"
#if CONFIG_BT_NIMBLE_MESH
#include "NimBLEMeshNode.h"
#include "NimBLELog.h"
#include "NimBLEDevice.h"
#include "NimBLEMeshCreateModel.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h"
# include "services/gatt/ble_svc_gatt.h"
#else
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
#endif
#define CID_VENDOR 0x05C3
static const char* LOG_TAG = "NimBLEMeshNode";
/**
* @brief Construct a mesh node.
* @param [in] uuid The uuid used to advertise for provisioning.
* @param [in] type Bitmask of the node features supported.
*/
NimBLEMeshNode::NimBLEMeshNode(const NimBLEUUID &uuid, uint8_t type) {
assert(uuid.bitSize() == 128);
memset(&m_serverConfig, 0, sizeof(m_serverConfig));
memset(&m_prov, 0, sizeof(m_prov));
memset(&m_comp, 0, sizeof(m_comp));
// Default server config
m_serverConfig.relay = BT_MESH_RELAY_DISABLED;/*(type & NIMBLE_MESH::RELAY) ?
BT_MESH_RELAY_ENABLED :
BT_MESH_RELAY_DISABLED;*/
m_serverConfig.beacon = BT_MESH_BEACON_ENABLED;
m_serverConfig.frnd = BT_MESH_FRIEND_DISABLED;/*(type & NIMBLE_MESH::FRIEND) ?
BT_MESH_FRIEND_ENABLED :
BT_MESH_FRIEND_DISABLED;*/
m_serverConfig.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED; /*(type & NIMBLE_MESH::RELAY) ?
BT_MESH_GATT_PROXY_ENABLED :
BT_MESH_GATT_PROXY_DISABLED;*/
m_serverConfig.default_ttl = 7;
// 3 transmissions with 20ms interval
m_serverConfig.net_transmit = BT_MESH_TRANSMIT(2, 20);
m_serverConfig.relay_retransmit = BT_MESH_TRANSMIT(2, 20);
// Provisioning config
m_uuid = uuid;
m_prov.uuid = m_uuid.getNative()->u128.value;
m_prov.complete = NimBLEMeshNode::provComplete;
m_prov.reset = NimBLEMeshNode::provReset;
// Create the primary element
m_elemVec.push_back(new NimBLEMeshElement());
}
/**
* @brief Destructor, cleanup any resources created.
*/
NimBLEMeshNode::~NimBLEMeshNode() {
if(m_comp.elem != nullptr) {
free (m_comp.elem);
}
}
/**
* @brief Called from the callbacks when provisioning changes.
*/
void NimBLEMeshNode::setProvData(uint16_t netIdx, uint16_t addr) {
m_primAddr = addr;
m_primNetIdx = netIdx;
}
/**
* @brief callback, Called by NimBLE stack when provisioning is complete.
*/
void NimBLEMeshNode::provComplete(uint16_t netIdx, uint16_t addr) {
NIMBLE_LOGI(LOG_TAG,
"provisioning complete for netIdx 0x%04x addr 0x%04x",
netIdx, addr);
NimBLEDevice::getMeshNode()->setProvData(netIdx, addr);
}
/**
* @brief callback, Called by NimBLE stack when provisioning is reset.
*/
void NimBLEMeshNode::provReset() {
NIMBLE_LOGI(LOG_TAG, "provisioning reset");
NimBLEDevice::getMeshNode()->setProvData(0, 0);
}
/**
* @brief get a pointer an element.
* @param [in] index The element vector index of the element.
* @returns a pointer to the element requested.
*/
NimBLEMeshElement* NimBLEMeshNode::getElement(uint8_t index) {
return m_elemVec[index];
}
/**
* @brief Create a new mesh element.
* @returns a pointer to the newly created element.
*/
NimBLEMeshElement* NimBLEMeshNode::createElement() {
m_elemVec.push_back(new NimBLEMeshElement());
return m_elemVec.back();
}
/**
* @brief Get a pointer to the health model instance that matches the ID's of the input model.
* @param [in] model A pointer to the NimBLE internal model instance.
* @returns A pointer to the model.
*/
NimBLEMeshModel* NimBLEMeshNode::getHealthModel(bt_mesh_model *model) {
NimBLEMeshModel* pModel;
for(auto &it : m_elemVec) {
pModel = it->getModelByIdx(model->elem_idx, model->mod_idx, BT_MESH_MODEL_ID_HEALTH_SRV);
if(pModel != nullptr) {
return pModel;
}
}
return nullptr;
}
/**
* @brief Start the Mesh mode.
* @returns true on success.
*/
bool NimBLEMeshNode::start() {
// Reset and restart gatts so we can register mesh gatt
ble_gatts_reset();
ble_svc_gap_init();
ble_svc_gatt_init();
bt_mesh_register_gatt();
ble_gatts_start();
// Config server and primary health models are required in the primary element
// create them here and add them as the first models.
m_elemVec[0]->addModel(createConfigSrvModel(&m_serverConfig));
if(m_elemVec[0]->getModel(BT_MESH_MODEL_ID_HEALTH_SRV) == nullptr) {
m_elemVec[0]->createModel(BT_MESH_MODEL_ID_HEALTH_SRV);
}
// setup node composition
m_comp.cid = CID_VENDOR;
m_comp.elem = (bt_mesh_elem*)calloc(m_elemVec.size(), sizeof(bt_mesh_elem));
if(m_comp.elem == nullptr) {
NIMBLE_LOGE(LOG_TAG, "Error: No Mem");
return false;
}
for(size_t i = 0; i < m_elemVec.size(); i++) {
memcpy((void*)&m_comp.elem[i], (void*)m_elemVec[i]->start(), sizeof(bt_mesh_elem));
}
m_comp.elem_count = (uint8_t)m_elemVec.size();
// Use random address
ble_addr_t addr;
int err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &m_prov, &m_comp);
if (err) {
NIMBLE_LOGE(LOG_TAG, "Initializing mesh failed (err %d)", err);
return false;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
NIMBLE_LOGI(LOG_TAG, "Mesh network restored from flash");
}
return true;
}
#endif // CONFIG_BT_NIMBLE_MESH

View File

@@ -1,76 +0,0 @@
/*
* NimBLEMeshNode.h
*
* Created: on July 22 2020
* Author H2zero
*
*/
#ifndef MAIN_NIMBLE_MESH_NODE_H_
#define MAIN_NIMBLE_MESH_NODE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "mesh/glue.h"
# include "mesh/mesh.h"
#else
# include "nimble/nimble/host/mesh/include/mesh/glue.h"
# include "nimble/nimble/host/mesh/include/mesh/mesh.h"
#endif
#pragma GCC diagnostic pop
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include "NimBLEUUID.h"
#include "NimBLEMeshElement.h"
#include <vector>
class NimBLEMeshModel;
typedef enum {
RELAY = 0x01 << 0,
BEACON = 0x01 << 1,
FRIEND = 0x01 << 2,
PROXY = 0x01 << 3,
} NIMBLE_MESH;
class NimBLEMeshElement;
class NimBLEMeshNode {
public:
bool start();
NimBLEMeshElement* createElement();
NimBLEMeshElement* getElement(uint8_t index = 0);
NimBLEMeshModel* getHealthModel(bt_mesh_model *model);
private:
friend class NimBLEDevice;
friend class NimBLEMeshElement;
NimBLEMeshNode(const NimBLEUUID &uuid, uint8_t type);
~NimBLEMeshNode();
static void provComplete(uint16_t netIdx, uint16_t addr);
static void provReset();
void setProvData(uint16_t netIdx, uint16_t addr);
bt_mesh_cfg_srv m_serverConfig;
bt_mesh_prov m_prov;
bt_mesh_comp m_comp;
uint16_t m_primAddr;
uint16_t m_primNetIdx;
NimBLEUUID m_uuid;
std::vector<NimBLEMeshElement*> m_elemVec;
};
#endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLE_MESH_NODE_H_

View File

@@ -58,6 +58,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_timestamp = 0;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
@@ -266,10 +267,6 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
}
}
if (m_handle == m_endHandle) {
return true;
}
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
@@ -434,12 +431,15 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
ble_npl_hw_enter_critical();
std::string value = m_value;
if(timestamp != nullptr) {
*timestamp = m_value.getTimeStamp();
*timestamp = m_timestamp;
}
ble_npl_hw_exit_critical(0);
return m_value;
return value;
}
@@ -487,12 +487,12 @@ float NimBLERemoteCharacteristic::readFloat() {
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle());
NimBLEClient* pClient = getRemoteService()->getClient();
NimBLEAttValue value;
std::string value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
@@ -543,11 +543,14 @@ NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
}
} while(rc != 0 && retryCount--);
value.setTimeStamp();
time_t t = time(nullptr);
ble_npl_hw_enter_critical();
m_value = value;
m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = value.getTimeStamp();
*timestamp = m_timestamp;
}
ble_npl_hw_exit_critical(0);
NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
return value;
@@ -572,17 +575,17 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
std::string *strBuf = (std::string*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
if(((*strBuf).length() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
valBuf->append(attr->om->om_data, data_len);
(*strBuf) += std::string((char*) attr->om->om_data, data_len);
return 0;
}
}
@@ -733,33 +736,22 @@ std::string NimBLERemoteCharacteristic::toString() {
/**
* @brief Write a new value to the remote characteristic from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
* @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 false if not connected or cant perform write for some reason.
*/
bool NimBLERemoteCharacteristic::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)&vec[0], vec.size(), response);
bool NimBLERemoteCharacteristic::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*)newValue.c_str(), newValue.length(), response);
} // writeValue
/**
* @brief Write a new value to the remote characteristic from a const char*.
* @param [in] char_s A character string to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to the remote characteristic from a data buffer.
* @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.
* @return false if not connected or otherwise cannot perform write.
* @return false if not connected or cant perform write for some reason.
*/
bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {

View File

@@ -23,7 +23,6 @@
#include <vector>
#include <functional>
#include "NimBLELog.h"
class NimBLERemoteService;
class NimBLERemoteDescriptor;
@@ -61,15 +60,47 @@ public:
uint16_t getHandle();
uint16_t getDefHandle();
NimBLEUUID getUUID();
NimBLEAttValue readValue(time_t *timestamp = nullptr);
std::string toString();
NimBLERemoteService* getRemoteService();
std::string readValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = readValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()")));
NimBLEAttValue getValue(time_t *timestamp = nullptr);
std::string getValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
bool subscribe(bool notifications = true,
notify_callback notifyCallback = nullptr,
@@ -82,74 +113,20 @@ public:
bool writeValue(const uint8_t* data,
size_t length,
bool response = false);
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool writeValue(const char* s, bool response = false);
/*********************** Template Functions ************************/
bool writeValue(const std::string &newValue,
bool response = false);
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @brief Convenience template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && m_value.size() < sizeof(T)) return T();
return *((T *)m_value.getValue(timestamp));
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
NimBLEAttValue value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
return *((T *)value.getValue(timestamp));
}
std::string toString();
NimBLERemoteService* getRemoteService();
private:
@@ -179,8 +156,9 @@ private:
uint16_t m_defHandle;
uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService;
NimBLEAttValue m_value;
std::string m_value;
notify_callback m_notifyCallback;
time_t m_timestamp;
// We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;

View File

@@ -86,7 +86,11 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
* @deprecated Use readValue<uint8_t>().
*/
uint8_t NimBLERemoteDescriptor::readUInt8() {
return readValue<uint8_t>();
std::string value = readValue();
if (value.length() >= 1) {
return (uint8_t) value[0];
}
return 0;
} // readUInt8
@@ -96,7 +100,11 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
* @deprecated Use readValue<uint16_t>().
*/
uint16_t NimBLERemoteDescriptor::readUInt16() {
return readValue<uint16_t>();
std::string value = readValue();
if (value.length() >= 2) {
return *(uint16_t*) value.data();
}
return 0;
} // readUInt16
@@ -106,7 +114,11 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
* @deprecated Use readValue<uint32_t>().
*/
uint32_t NimBLERemoteDescriptor::readUInt32() {
return readValue<uint32_t>();
std::string value = readValue();
if (value.length() >= 4) {
return *(uint32_t*) value.data();
}
return 0;
} // readUInt32
@@ -114,11 +126,11 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
* @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor.
*/
NimBLEAttValue NimBLERemoteDescriptor::readValue() {
std::string NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
NimBLEAttValue value;
std::string value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
@@ -192,17 +204,17 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
std::string *strBuf = (std::string*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
if(((*strBuf).length() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
valBuf->append(attr->om->om_data, data_len);
(*strBuf) += std::string((char*) attr->om->om_data, data_len);
return 0;
}
}
@@ -255,33 +267,11 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
/**
* @brief Write a new value to a remote descriptor from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)&vec[0], vec.size(), response);
} // writeValue
/**
* @brief Write a new value to the remote descriptor from a const char*.
* @param [in] char_s A character string to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to a remote descriptor.
* @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 write response.
* @return false if not connected or otherwise cannot perform write.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
@@ -362,4 +352,14 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
} // 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.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@@ -29,53 +29,10 @@ public:
uint16_t getHandle();
NimBLERemoteCharacteristic* getRemoteCharacteristic();
NimBLEUUID getUUID();
NimBLEAttValue readValue();
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool writeValue(const char* s, bool response = false);
/*********************** Template Functions ************************/
std::string readValue();
/**
* @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
/**
* @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote descriptor data to <type\>.
* @brief A template to convert the remote descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
@@ -83,10 +40,28 @@ public:
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
*/
template<typename T>
T readValue(bool skipSizeCheck = false) {
NimBLEAttValue value = readValue();
T readValue(bool skipSizeCheck = false) {
std::string value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
return *((T *)value.data());
const char *pData = value.data();
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
/**
* @brief Convenience template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
private:

View File

@@ -256,9 +256,7 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
}
}
if (m_characteristicVector.size() > 0) {
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");

View File

@@ -56,27 +56,18 @@ NimBLEScan::~NimBLEScan() {
* @param [in] param Parameter data for this event.
*/
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
(void)arg;
NimBLEScan* pScan = NimBLEDevice::getScan();
NimBLEScan* pScan = (NimBLEScan*)arg;
switch(event->type) {
case BLE_GAP_EVENT_EXT_DISC:
case BLE_GAP_EVENT_DISC: {
if(pScan->m_ignoreResults) {
NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
#if CONFIG_BT_NIMBLE_EXT_ADV
const auto& disc = event->ext_disc;
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
#else
const auto& disc = event->disc;
const bool isLegacyAdv = true;
const auto event_type = disc.event_type;
#endif
NimBLEAddress advertisedAddress(disc.addr);
NimBLEAddress advertisedAddress(event->disc.addr);
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
if(NimBLEDevice::isIgnored(advertisedAddress)) {
@@ -88,12 +79,7 @@ NimBLEScan::~NimBLEScan() {
// If we've seen this device before get a pointer to it from the vector
for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) {
#if CONFIG_BT_NIMBLE_EXT_ADV
// Same address but different set ID should create a new advertised device.
if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) {
#else
if (it->getAddress() == advertisedAddress) {
#endif
if(it->getAddress() == advertisedAddress) {
advertisedDevice = it;
break;
}
@@ -101,27 +87,20 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device.
if (advertisedDevice == nullptr &&
(!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) {
if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){
// Check if we have reach the scan results limit, ignore this one if so.
// We still need to store each device when maxResults is 0 to be able to append the scan results
if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
(pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) {
if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
(pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults))
{
return 0;
}
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event_type, isLegacyAdv);
#if CONFIG_BT_NIMBLE_EXT_ADV
advertisedDevice->setSetId(disc.sid);
advertisedDevice->setPrimaryPhy(disc.prim_phy);
advertisedDevice->setSecondaryPhy(disc.sec_phy);
advertisedDevice->setPeriodicInterval(disc.periodic_adv_itvl);
#endif
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else if (advertisedDevice != nullptr) {
} else if(advertisedDevice != nullptr) {
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
@@ -129,14 +108,14 @@ NimBLEScan::~NimBLEScan() {
}
advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(disc.rssi);
advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv &&
event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP));
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
if (pScan->m_pAdvertisedDeviceCallbacks) {
// If not active scanning or scan response is not available
// or extended advertisement scanning, report the result to the callback now.
if(pScan->m_scan_params.passive || !isLegacyAdv ||
// report the result to the callback now.
if(pScan->m_scan_params.passive ||
(advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
{
@@ -144,7 +123,7 @@ NimBLEScan::~NimBLEScan() {
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise, wait for the scan response so we can report the complete data.
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
@@ -325,28 +304,9 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
m_ignoreResults = true;
}
# if CONFIG_BT_NIMBLE_EXT_ADV
ble_gap_ext_disc_params scan_params;
scan_params.passive = m_scan_params.passive;
scan_params.itvl = m_scan_params.itvl;
scan_params.window = m_scan_params.window;
int rc = ble_gap_ext_disc(NimBLEDevice::m_own_addr_type,
duration/10,
0,
m_scan_params.filter_duplicates,
m_scan_params.filter_policy,
m_scan_params.limited,
&scan_params,
&scan_params,
NimBLEScan::handleGapEvent,
NULL);
#else
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type,
duration,
&m_scan_params,
NimBLEScan::handleGapEvent,
NULL);
#endif
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this);
switch(rc) {
case 0:
if(!is_continue) {

View File

@@ -42,9 +42,7 @@ NimBLEServer::NimBLEServer() {
// m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
#if !CONFIG_BT_NIMBLE_EXT_ADV
m_advertiseOnDisconnect = true;
#endif
m_svcChanged = false;
m_deleteCallbacks = true;
} // NimBLEServer
@@ -141,26 +139,15 @@ NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
return nullptr;
}
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return An advertising object.
*/
NimBLEExtAdvertising* NimBLEServer::getAdvertising() {
return NimBLEDevice::getAdvertising();
} // getAdvertising
#endif
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
*
* @return An advertising object.
*/
NimBLEAdvertising* NimBLEServer::getAdvertising() {
return NimBLEDevice::getAdvertising();
} // getAdvertising
#endif
/**
* @brief Sends a service changed notification and resets the GATT server.
@@ -253,7 +240,6 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
} // disconnect
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] aod true == advertise, false == don't advertise.
@@ -261,7 +247,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
} // advertiseOnDisconnect
#endif
/**
* @brief Return the number of connected clients.
@@ -339,7 +325,7 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
*/
/*STATIC*/
int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEServer* server = NimBLEDevice::getServer();
NimBLEServer* server = (NimBLEServer*)arg;
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
NimBLEUtils::gapEventToString(event->type));
int rc = 0;
@@ -351,9 +337,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
NIMBLE_LOGE(LOG_TAG, "Connection failed");
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::startAdvertising();
#endif
}
else {
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
@@ -398,11 +382,9 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
#if !CONFIG_BT_NIMBLE_EXT_ADV
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
}
#endif
return 0;
} // BLE_GAP_EVENT_DISCONNECT
@@ -490,15 +472,11 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE:
#if CONFIG_BT_NIMBLE_EXT_ADV
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
return NimBLEExtAdvertising::handleGapEvent(event, arg);
#else
return NimBLEAdvertising::handleGapEvent(event, arg);
#endif
// BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
case BLE_GAP_EVENT_ADV_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "Advertising Complete");
NimBLEDevice::getAdvertising()->advCompleteCB();
return 0;
}
case BLE_GAP_EVENT_CONN_UPDATE: {
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
@@ -675,9 +653,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
serviceChanged();
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
#endif
}
@@ -740,52 +716,22 @@ void NimBLEServer::resetGATT() {
}
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Start advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
*
* Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
bool NimBLEServer::startAdvertising(uint8_t inst_id,
int duration,
int max_events) {
return getAdvertising()->start(inst_id, duration, max_events);
void NimBLEServer::startAdvertising() {
NimBLEDevice::startAdvertising();
} // startAdvertising
/**
* @brief Convenience function to stop advertising a data set.
* @param [in] inst_id The extended advertisement instance ID to stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEServer::stopAdvertising(uint8_t inst_id) {
return getAdvertising()->stop(inst_id);
} // stopAdvertising
#endif
#if !CONFIG_BT_NIMBLE_EXT_ADV|| defined(_DOXYGEN_)
/**
* @brief Start advertising.
* @return True if advertising started successfully.
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
bool NimBLEServer::startAdvertising() {
return getAdvertising()->start();
} // startAdvertising
#endif
/**
* @brief Stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEServer::stopAdvertising() {
return getAdvertising()->stop();
void NimBLEServer::stopAdvertising() {
NimBLEDevice::stopAdvertising();
} // stopAdvertising

View File

@@ -25,11 +25,7 @@
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
#if CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h"
#else
#include "NimBLEAdvertising.h"
#endif
#include "NimBLEService.h"
#include "NimBLESecurity.h"
#include "NimBLEConnInfo.h"
@@ -50,19 +46,11 @@ public:
NimBLEService* createService(const NimBLEUUID &uuid);
void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service);
NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks,
bool deleteCallbacks = true);
#if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* getAdvertising();
bool startAdvertising(uint8_t inst_id,
int duration = 0,
int max_events = 0);
bool stopAdvertising(uint8_t inst_id);
#else
NimBLEAdvertising* getAdvertising();
bool startAdvertising();
#endif
bool stopAdvertising();
void startAdvertising();
void stopAdvertising();
void start();
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
@@ -78,9 +66,7 @@ public:
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool);
#endif
private:
NimBLEServer();
@@ -89,15 +75,9 @@ private:
friend class NimBLEService;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
#if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisementData;
#endif
bool m_gattsStarted;
#if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect;
#endif
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;

View File

@@ -256,11 +256,10 @@ uint16_t NimBLEService::getHandle() {
* @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.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic.
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties) {
return createCharacteristic(NimBLEUUID(uuid), properties);
}
@@ -268,11 +267,10 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
* @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.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic.
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this);
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",

View File

@@ -50,14 +50,12 @@ public:
NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NIMBLE_PROPERTY::WRITE);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NIMBLE_PROPERTY::WRITE);
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);

View File

@@ -54,22 +54,6 @@ int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
return 0;
}
ble_npl_time_t NimBLEUtils::meshTransTimeMs(uint8_t tt) {
switch(tt >> 6) {
case 0:
return 100;
case 1:
return 1000;
case 2:
return 10000;
case 3:
return 600000;
default:
return 0;
}
}
/**
* @brief Converts a return code from the NimBLE stack to a text string.

View File

@@ -29,7 +29,7 @@ typedef struct {
void *pATT;
TaskHandle_t task;
int rc;
void *buf;
std::string *buf;
} ble_task_data_t;
@@ -44,7 +44,6 @@ public:
static const char* advTypeToString(uint8_t advType);
static const char* returnCodeToString(int rc);
static int checkConnParams(ble_gap_conn_params* params);
static ble_npl_time_t meshTransTimeMs(uint8_t tt);
};

View File

@@ -1,295 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 __UTIL_BASE64_H
#define __UTIL_BASE64_H
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
struct base64_decoder {
/*** public */
const char *src;
void *dst;
int src_len; /* <=0 if src ends with '\0' */
int dst_len; /* <=0 if dst unbounded */
/*** private */
char buf[4];
int buf_len;
};
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int
pos(char c)
{
const char *p;
for (p = base64_chars; *p; p++)
if (*p == c)
return p - base64_chars;
return -1;
}
int
base64_encode(const void *data, int size, char *s, uint8_t should_pad)
{
char *p;
int i;
int c;
const unsigned char *q;
char *last;
int diff;
p = s;
q = (const unsigned char *) data;
last = NULL;
i = 0;
while (i < size) {
c = q[i++];
c *= 256;
if (i < size)
c += q[i];
i++;
c *= 256;
if (i < size)
c += q[i];
i++;
p[0] = base64_chars[(c & 0x00fc0000) >> 18];
p[1] = base64_chars[(c & 0x0003f000) >> 12];
p[2] = base64_chars[(c & 0x00000fc0) >> 6];
p[3] = base64_chars[(c & 0x0000003f) >> 0];
last = p;
p += 4;
}
if (last) {
diff = i - size;
if (diff > 0) {
if (should_pad) {
memset(last + (4 - diff), '=', diff);
} else {
p = last + (4 - diff);
}
}
}
*p = 0;
return (p - s);
}
int
base64_pad(char *buf, int len)
{
int remainder;
remainder = len % 4;
if (remainder == 0) {
return (0);
}
memset(buf, '=', 4 - remainder);
return (4 - remainder);
}
#define DECODE_ERROR -1
static unsigned int
token_decode(const char *token, int len)
{
int i;
unsigned int val = 0;
int marker = 0;
if (len < 4) {
return DECODE_ERROR;
}
for (i = 0; i < 4; i++) {
val *= 64;
if (token[i] == '=') {
marker++;
} else if (marker > 0) {
return DECODE_ERROR;
} else {
val += pos(token[i]);
}
}
if (marker > 2) {
return DECODE_ERROR;
}
return (marker << 24) | val;
}
int
base64_decoder_go(struct base64_decoder *dec)
{
unsigned int marker;
unsigned int val;
uint8_t *dst;
char sval;
int read_len;
int src_len;
int src_rem;
int src_off;
int dst_len;
int dst_off;
int i;
dst = dec->dst;
dst_off = 0;
src_off = 0;
/* A length <= 0 means "unbounded". */
if (dec->src_len <= 0) {
src_len = INT_MAX;
} else {
src_len = dec->src_len;
}
if (dec->dst_len <= 0) {
dst_len = INT_MAX;
} else {
dst_len = dec->dst_len;
}
while (1) {
src_rem = src_len - src_off;
if (src_rem == 0) {
/* End of source input. */
break;
}
if (dec->src[src_off] == '\0') {
/* End of source string. */
break;
}
/* Account for possibility of partial token from previous call. */
read_len = 4 - dec->buf_len;
/* Detect invalid input. */
for (i = 0; i < read_len; i++) {
sval = dec->src[src_off + i];
if (sval == '\0') {
/* Incomplete input. */
return -1;
}
if (sval != '=' && strchr(base64_chars, sval) == NULL) {
/* Invalid base64 character. */
return -1;
}
}
if (src_rem < read_len) {
/* Input contains a partial token. Stash it for use during the
* next call.
*/
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], src_rem);
dec->buf_len += src_rem;
break;
}
/* Copy full token into buf and decode it. */
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], read_len);
val = token_decode(dec->buf, read_len);
if (val == DECODE_ERROR) {
return -1;
}
src_off += read_len;
dec->buf_len = 0;
marker = (val >> 24) & 0xff;
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = (val >> 16) & 0xff;
dst_off++;
if (marker < 2) {
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = (val >> 8) & 0xff;
dst_off++;
}
if (marker < 1) {
if (dst_off >= dst_len) {
break;
}
dst[dst_off] = val & 0xff;
dst_off++;
}
}
return dst_off;
}
int
base64_decode(const char *str, void *data)
{
struct base64_decoder dec = {
.src = str,
.dst = data,
};
return base64_decoder_go(&dec);
}
int
base64_decode_maxlen(const char *str, void *data, int len)
{
struct base64_decoder dec = {
.src = str,
.dst = data,
.dst_len = len,
};
return base64_decoder_go(&dec);
}
int
base64_decode_len(const char *str)
{
int len;
len = strlen(str);
while (len && str[len - 1] == '=') {
len--;
}
return len * 3 / 4;
}
#define BASE64_ENCODE_SIZE(__size) (((((__size) - 1) / 3) * 4) + 4)
#ifdef __cplusplus
}
#endif
#endif /* __UTIL_BASE64_H__ */

View File

@@ -1,238 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 __SYS_CONFIG_H_
#define __SYS_CONFIG_H_
#include "../../nimconfig.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include <os/queue.h>
#else
# include "nimble/porting/nimble/include/os/queue.h"
#endif
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CONF_MAX_DIR_DEPTH 8 /* max depth of config tree */
#define CONF_MAX_NAME_LEN (8 * CONF_MAX_DIR_DEPTH)
/**
* Type of configuration value.
*/
typedef enum conf_type {
CONF_NONE = 0,
CONF_DIR,
/** 8-bit signed integer */
CONF_INT8,
/** 16-bit signed integer */
CONF_INT16,
/** 32-bit signed integer */
CONF_INT32,
/** 64-bit signed integer */
CONF_INT64,
/** String */
CONF_STRING,
/** Bytes */
CONF_BYTES,
/** Floating point */
CONF_FLOAT,
/** Double precision */
CONF_DOUBLE,
/** Boolean */
CONF_BOOL,
/** 8-bit unsigned integer */
CONF_UINT8,
/** 16-bit unsigned integer */
CONF_UINT16,
/** 32-bit unsigned integer */
CONF_UINT32,
/** 64-bit unsigned integer */
CONF_UINT64,
} __attribute__((__packed__)) conf_type_t;
/**
* Parameter to commit handler describing where data is going to.
*/
enum conf_export_tgt {
/** Value is to be persisted */
CONF_EXPORT_PERSIST,
/** Value is to be display */
CONF_EXPORT_SHOW
};
typedef enum conf_export_tgt conf_export_tgt_t;
/**
* Handler for getting configuration items, this handler is called
* per-configuration section. Configuration sections are delimited
* by '/', for example:
*
* - section/name/value
*
* Would be passed as:
*
* - argc = 3
* - argv[0] = section
* - argv[1] = name
* - argv[2] = value
*
* The handler returns the value into val, null terminated, up to
* val_len_max.
*
* @param argc The number of sections in the configuration variable
* @param argv The array of configuration sections
* @param val A pointer to the buffer to return the configuration
* value into.
* @param val_len_max The maximum length of the val buffer to copy into.
*
* @return A pointer to val or NULL if error.
*/
typedef char *(*conf_get_handler_t)(int argc, char **argv, char *val, int val_len_max);
typedef char *(*conf_get_handler_ext_t)(int argc, char **argv, char *val, int val_len_max, void *arg);
/**
* Set the configuration variable pointed to by argc and argv. See
* description of ch_get_handler_t for format of these variables. This sets the
* configuration variable to the shadow value, but does not apply the configuration
* change. In order to apply the change, call the ch_commit() handler.
*
* @param argc The number of sections in the configuration variable.
* @param argv The array of configuration sections
* @param val The value to configure that variable to
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_set_handler_t)(int argc, char **argv, char *val);
typedef int (*conf_set_handler_ext_t)(int argc, char **argv, char *val, void *arg);
/**
* Commit shadow configuration state to the active configuration.
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_commit_handler_t)(void);
typedef int (*conf_commit_handler_ext_t)(void *arg);
/**
* Called per-configuration variable being exported.
*
* @param name The name of the variable to export
* @param val The value of the variable to export
*/
typedef void (*conf_export_func_t)(char *name, char *val);
/**
* Export all of the configuration variables, calling the export_func
* per variable being exported.
*
* @param export_func The export function to call.
* @param tgt The target of the export, either for persistence or display.
*
* @return 0 on success, non-zero error code on failure.
*/
typedef int (*conf_export_handler_t)(conf_export_func_t export_func,
conf_export_tgt_t tgt);
typedef int (*conf_export_handler_ext_t)(conf_export_func_t export_func,
conf_export_tgt_t tgt, void *arg);
/**
* Configuration handler, used to register a config item/subtree.
*/
struct conf_handler {
SLIST_ENTRY(conf_handler) ch_list;
/**
* The name of the conifguration item/subtree
*/
char *ch_name;
/**
* Whether to use the extended callbacks.
* false: standard
* true: extended
*/
bool ch_ext;
/** Get configuration value */
union {
conf_get_handler_t ch_get;
conf_get_handler_ext_t ch_get_ext;
};
/** Set configuration value */
union {
conf_set_handler_t ch_set;
conf_set_handler_ext_t ch_set_ext;
};
/** Commit configuration value */
union {
conf_commit_handler_t ch_commit;
conf_commit_handler_ext_t ch_commit_ext;
};
/** Export configuration value */
union {
conf_export_handler_t ch_export;
conf_export_handler_ext_t ch_export_ext;
};
/** Custom argument that gets passed to the extended callbacks */
void *ch_arg;
};
/**
* Register a handler for configurations items.
*
* @param cf Structure containing registration info.
*
* @return 0 on success, non-zero on failure.
*/
int conf_register(struct conf_handler *cf);
/**
* Load configuration from registered persistence sources. Handlers for
* configuration subtrees registered earlier will be called for encountered
* values.
*
* @return 0 on success, non-zero on failure.
*/
int conf_load(void);
/**
* Write a single configuration value to persisted storage (if it has
* changed value).
*
* @param name Name/key of the configuration item.
* @param var Value of the configuration item.
*
* @return 0 on success, non-zero on failure.
*/
int conf_save_one(const char *name, char *var);
#ifdef __cplusplus
}
#endif
#define SYSINIT_PANIC_ASSERT_MSG(rc, msg) assert(rc)
#endif /* __SYS_CONFIG_H_ */

View File

@@ -1,136 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "nimconfig.h"
#ifdef ESP_PLATFORM
#if CONFIG_BT_NIMBLE_MESH && CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS
#include "config.h"
#include "nvs.h"
#include <string.h>
static struct conf_handler* config_handler;
int conf_parse_name(char *name, int *name_argc, char *name_argv[])
{
char *tok;
char *tok_ptr;
const char *sep = "/";
int i;
tok = strtok_r(name, sep, &tok_ptr);
i = 0;
while (tok) {
name_argv[i++] = tok;
tok = strtok_r(NULL, sep, &tok_ptr);
}
*name_argc = i;
return 0;
}
int conf_load(void)
{
esp_err_t err;
nvs_handle handle;
err = nvs_open(config_handler->ch_name, NVS_READONLY, &handle);
if (err != ESP_OK) return err;
nvs_iterator_t it = nvs_entry_find("nvs", config_handler->ch_name, NVS_TYPE_ANY);
while (it != NULL) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
size_t required_size = 0;
err = nvs_get_str(handle, info.key, NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
char* val = malloc(required_size);
if (required_size > 0) {
err = nvs_get_str(handle, info.key, val, &required_size);
if (err != ESP_OK) {
free(val);
return err;
}
}
int name_argc;
char *name_argv[8];
conf_parse_name(info.key, &name_argc, name_argv);
config_handler->ch_set(name_argc, &name_argv[0], val);
free(val);
}
nvs_close(handle);
config_handler->ch_commit();
return ESP_OK;
}
int conf_save_one(const char *name, char *var)
{
esp_err_t err;
nvs_handle_t handle;
int name_argc;
char *name_argv[CONF_MAX_DIR_DEPTH];
char n[CONF_MAX_NAME_LEN];
strcpy(n, name);
conf_parse_name(n, &name_argc, name_argv);
err = nvs_open(name_argv[0], NVS_READWRITE, &handle);
if (err != ESP_OK) return err;
const char* key = name_argv[1];
if (name_argc > 2) {
key = name;
while (*key != '/') {
key++;
}
key++;
}
if (var) {
err = nvs_set_str(handle, key, var);
if (err != ESP_OK) return err;
} else {
err = nvs_erase_key(handle, key);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
}
err = nvs_commit(handle);
if (err != ESP_OK) return err;
nvs_close(handle);
return ESP_OK;
}
int conf_register(struct conf_handler *cf)
{
config_handler = cf;
return 0;
}
#endif // CONFIG_BT_NIMBLE_MESH && MYNEWT_VAL_BLE_MESH_SETTINGS
#endif // ESP_PLATFORM

View File

@@ -25,13 +25,6 @@
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
/* Enables the use of Arduino String class for attribute values */
#if defined __has_include
# if __has_include (<Arduino.h>)
# define NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# endif
#endif
#endif /* CONFIG_BT_ENABLED */
#ifdef _DOXYGEN_
@@ -39,22 +32,6 @@
/** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
/** @brief Un-comment to enable storing the timestamp when an attribute value is updated\n
* This allows for checking the last update time using getTimeStamp() or getValue(time_t*)\n
* If disabled, the timestamp returned from these functions will be 0.\n
* Disabling timestamps will reduce the memory used for each value.\n
* 1 = Enabled, 0 = Disabled; Default = Disabled
*/
#define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
/** @brief Uncomment to set the default allocation size (bytes) for each attribute if\n
* not specified when the constructor is called. This is also the size used when a remote\n
* characteristic or descriptor is constructed before a value is read/notifed.\n
* Increasing this will reduce reallocations but increase memory footprint.\n
* Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
*/
#define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
/** @brief Un-comment to change the default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
@@ -131,7 +108,6 @@
/** @brief Un-comment to use external PSRAM for the NimBLE host */
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1
/** @brief Un-comment to change the core NimBLE host runs on */
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0