mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2024-11-22 05:00:55 +01:00
[Breaking] Update callbacks to use NimBLEConnInfo.
Change the callback functions that receive a ble_gap_conn_desc pointer to instead receive a NimBLEConnInfo reference. * Add a reason parameter to the server disconnect callback. * Remove connect and disconnect callback that do not receive info parameters. * Remove onRead and onWrite Characteristic callbacks that do not receive info parameters. * Add info parameter to Descriptor onWrite and onRead callbacks. * Add details to migration guide.
This commit is contained in:
parent
ca8a7c56ac
commit
ba79a1bf72
17 changed files with 272 additions and 287 deletions
|
@ -9,8 +9,10 @@ For more information on the improvements and additions please refer to the [clas
|
|||
* [General Changes](#general-information)
|
||||
* [Server](#server-api)
|
||||
* [Services](#services)
|
||||
* [characteristics](#characteristics)
|
||||
* [descriptors](#descriptors)
|
||||
* [Characteristics](#characteristics)
|
||||
* [Characteristic Callbacks](#characteristic-callbacks)
|
||||
* [Descriptors](#descriptors)
|
||||
* [Descriptor Callbacks](#descriptor-callbacks)
|
||||
* [Security](#server-security)
|
||||
* [Advertising](#advertising-api)
|
||||
* [Client](#client-api)
|
||||
|
@ -46,8 +48,7 @@ For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address obje
|
|||
|
||||
As this parameter is optional no changes to existing code are needed, it is mentioned here for information.
|
||||
|
||||
`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. In this library the address bytes are stored in reverse order from the original library. This is due to the way the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are
|
||||
not affected as the endian change is made within them.
|
||||
`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. In this library the address bytes are stored in reverse order from the original library. This is due to the way the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are not affected as the endian change is made within them.
|
||||
<br/>
|
||||
|
||||
<a name="server-api"></a>
|
||||
|
@ -56,6 +57,27 @@ Creating a `BLEServer` instance is the same as original, no changes required.
|
|||
For example `BLEDevice::createServer()` will work just as it did before.
|
||||
|
||||
`BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations.
|
||||
<br/>
|
||||
|
||||
`BLEServerCallbacks::onConnect` (`NimBLEServerCallbacks::onConnect`) only has a single callback declaration which takes an additional (required) parameter `NimBLEConnInfo & connInfo`, which has methods to get information about the connected peer.
|
||||
```
|
||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo)`
|
||||
```
|
||||
<br/>
|
||||
|
||||
`BLEServerCallbacks::onDisconnect` (`NimBLEServerCallbacks::onDisconnect`) only has a single callback declaration which takes 2 additional (required) parameters `NimBLEConnInfo & connInfo`, which provides information about the peer and `int reason`, which gives the reason code for disconnection.
|
||||
|
||||
```
|
||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason)`
|
||||
```
|
||||
<br/>
|
||||
|
||||
`BLEServerCallbacks::onMtuChanged` (`NimBLEServerCallbacks::onMtuChanged`) takes the parameter `NimBLEConnInfo & connInfo` instead of `esp_ble_gatts_cb_param_t`, which has methods to get information about the connected peer.
|
||||
|
||||
```
|
||||
onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
|
||||
```
|
||||
|
||||
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
|
||||
<br/>
|
||||
|
||||
|
@ -63,6 +85,7 @@ For example `BLEDevice::createServer()` will work just as it did before.
|
|||
### Services
|
||||
Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required.
|
||||
For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
|
||||
<br/>
|
||||
|
||||
<a name="characteristics"></a>
|
||||
### Characteristics
|
||||
|
@ -72,26 +95,26 @@ When creating a characteristic the properties are now set with `NIMBLE_PROPERTY:
|
|||
|
||||
#### Originally
|
||||
> BLECharacteristic::PROPERTY_READ |
|
||||
> BLECharacteristic::PROPERTY_WRITE
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
|
||||
#### Is Now
|
||||
> NIMBLE_PROPERTY::READ |
|
||||
> NIMBLE_PROPERTY::WRITE
|
||||
NIMBLE_PROPERTY::WRITE
|
||||
<br/>
|
||||
|
||||
#### The full list of properties
|
||||
> NIMBLE_PROPERTY::READ
|
||||
> NIMBLE_PROPERTY::READ_ENC
|
||||
> NIMBLE_PROPERTY::READ_AUTHEN
|
||||
> NIMBLE_PROPERTY::READ_AUTHOR
|
||||
> NIMBLE_PROPERTY::WRITE
|
||||
> NIMBLE_PROPERTY::WRITE_NR
|
||||
> NIMBLE_PROPERTY::WRITE_ENC
|
||||
> NIMBLE_PROPERTY::WRITE_AUTHEN
|
||||
> NIMBLE_PROPERTY::WRITE_AUTHOR
|
||||
> NIMBLE_PROPERTY::BROADCAST
|
||||
> NIMBLE_PROPERTY::NOTIFY
|
||||
> NIMBLE_PROPERTY::INDICATE
|
||||
NIMBLE_PROPERTY::READ_ENC
|
||||
NIMBLE_PROPERTY::READ_AUTHEN
|
||||
NIMBLE_PROPERTY::READ_AUTHOR
|
||||
NIMBLE_PROPERTY::WRITE
|
||||
NIMBLE_PROPERTY::WRITE_NR
|
||||
NIMBLE_PROPERTY::WRITE_ENC
|
||||
NIMBLE_PROPERTY::WRITE_AUTHEN
|
||||
NIMBLE_PROPERTY::WRITE_AUTHOR
|
||||
NIMBLE_PROPERTY::BROADCAST
|
||||
NIMBLE_PROPERTY::NOTIFY
|
||||
NIMBLE_PROPERTY::INDICATE
|
||||
|
||||
<br/>
|
||||
|
||||
|
@ -114,9 +137,15 @@ BLECharacteristic *pCharacteristic = pService->createCharacteristic(
|
|||
```
|
||||
<br/>
|
||||
|
||||
<a name="characteristic-callbacks"></a>
|
||||
#### Characteristic callbacks
|
||||
`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` which is called when a client subscribes to notifications/indications.
|
||||
|
||||
`NimBLECharacteristicCallbacks::onStatus` (`NimBLECharacteristicCallbacks::onStatus`) has had the status parameter removed as it was unnecessary since the status code from the BLE stack was also provided. The status code for success is 0 for notifications and BLE_HS_EDONE for indications, any other value is an error.
|
||||
`BLECharacteristicCallbacks::onRead` (`NimBLECharacteristicCallbacks::onRead`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer.
|
||||
|
||||
`BLECharacteristicCallbacks::onWrite` (`NimBLECharacteristicCallbacks::onWrite`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer.
|
||||
|
||||
`BLECharacteristicCallbacks::onStatus` (`NimBLECharacteristicCallbacks::onStatus`) has had the status parameter removed as it was unnecessary since the status code from the BLE stack was also provided. The status code for success is 0 for notifications and BLE_HS_EDONE for indications, any other value is an error.
|
||||
|
||||
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
|
||||
<br/>
|
||||
|
@ -190,15 +219,24 @@ p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
|
|||
```
|
||||
<br/>
|
||||
|
||||
<a name="descriptor-callbacks"></a>
|
||||
#### Descriptor callbacks
|
||||
|
||||
> `BLEDescriptorCallbacks::onRead` (`NimBLEDescriptorCallbacks::onRead`)
|
||||
`BLEDescriptorCallbacks::onwrite` (`NimBLEDescriptorCallbacks::onwrite`)
|
||||
|
||||
The above descriptor callbacks take an additional (required) parameter `NimBLEConnInfo& connInfo`, which contains the connection information of the peer.
|
||||
<br/>
|
||||
|
||||
<a name="server-security"></a>
|
||||
### Server Security
|
||||
Security is set on the characteristic or descriptor properties by applying one of the following:
|
||||
> NIMBLE_PROPERTY::READ_ENC
|
||||
> NIMBLE_PROPERTY::READ_AUTHEN
|
||||
> NIMBLE_PROPERTY::READ_AUTHOR
|
||||
> NIMBLE_PROPERTY::WRITE_ENC
|
||||
> NIMBLE_PROPERTY::WRITE_AUTHEN
|
||||
> NIMBLE_PROPERTY::WRITE_AUTHOR
|
||||
NIMBLE_PROPERTY::READ_AUTHEN
|
||||
NIMBLE_PROPERTY::READ_AUTHOR
|
||||
NIMBLE_PROPERTY::WRITE_ENC
|
||||
NIMBLE_PROPERTY::WRITE_AUTHEN
|
||||
NIMBLE_PROPERTY::WRITE_AUTHOR
|
||||
|
||||
<br/>
|
||||
|
||||
|
@ -325,6 +363,7 @@ Also now returns a pointer to `std::vector` instead of `std::map`.
|
|||
> `BLEClientCallbacks::onDisconnect` (`NimBLEClientCallbacks::onDisconnect`)
|
||||
|
||||
This now takes a second parameter `int reason` which provides the reason code for disconnection.
|
||||
<br/>
|
||||
|
||||
<a name="client-security"></a>
|
||||
### Client Security
|
||||
|
@ -333,16 +372,14 @@ The default configuration will use "just-works" pairing with no bonding, if you
|
|||
<br/>
|
||||
|
||||
<a name="scan-api"></a>
|
||||
### BLE Scan
|
||||
## BLE Scan
|
||||
The scan API is mostly unchanged from the original except for `NimBLEScan::start`, in which the duration parameter is now in milliseconds instead of seconds.
|
||||
<br/>
|
||||
|
||||
<a name="security-api"></a>
|
||||
## Security API
|
||||
Security operations have been moved to `BLEDevice` (`NimBLEDevice`).
|
||||
|
||||
Also security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
|
||||
However backward compatibility with the original `BLESecurity` (`NimBLESecurity`) class is retained to minimize application code changes.
|
||||
The security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
|
||||
|
||||
The callback methods are:
|
||||
|
||||
|
@ -357,9 +394,9 @@ For server callback; return the passkey expected from the client.
|
|||
For client callback; return the passkey to send to the server.
|
||||
<br/>
|
||||
|
||||
> `void onAuthenticationComplete(ble_gap_conn_desc\* desc)`
|
||||
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
|
||||
|
||||
Authentication complete, success or failed information is in `desc`.
|
||||
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
|
||||
<br/>
|
||||
|
||||
Security settings and IO capabilities are now set by the following methods of NimBLEDevice.
|
||||
|
|
|
@ -19,15 +19,9 @@ static NimBLEServer* pServer;
|
|||
/** None of these are required as they will be handled by the library with defaults. **
|
||||
** Remove as you see fit for your needs */
|
||||
class ServerCallbacks: public NimBLEServerCallbacks {
|
||||
void onConnect(NimBLEServer* pServer) {
|
||||
printf("Client connected\n");
|
||||
NimBLEDevice::startAdvertising();
|
||||
};
|
||||
/** Alternative onConnect() method to extract details of the connection.
|
||||
* See: src/ble_gap.h for the details of the ble_gap_conn_desc struct.
|
||||
*/
|
||||
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
printf("Client address: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
|
||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
||||
printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
|
||||
|
||||
/** We can use the connection handle here to ask for different connection parameters.
|
||||
* Args: connection handle, min connection interval, max connection interval
|
||||
* latency, supervision timeout.
|
||||
|
@ -35,14 +29,17 @@ class ServerCallbacks: public NimBLEServerCallbacks {
|
|||
* Latency: number of intervals allowed to skip.
|
||||
* Timeout: 10 millisecond increments, try for 3x interval time for best results.
|
||||
*/
|
||||
pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 18);
|
||||
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 18);
|
||||
};
|
||||
void onDisconnect(NimBLEServer* pServer) {
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
||||
printf("Client disconnected - start advertising\n");
|
||||
NimBLEDevice::startAdvertising();
|
||||
};
|
||||
void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
|
||||
printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
|
||||
|
||||
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) {
|
||||
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
|
||||
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 60);
|
||||
};
|
||||
|
||||
/********************* Security handled here **********************
|
||||
|
@ -61,11 +58,10 @@ class ServerCallbacks: public NimBLEServerCallbacks {
|
|||
return true;
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc* desc){
|
||||
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||
/** Check that encryption was successful, if not we disconnect the client */
|
||||
if(!desc->sec_state.encrypted) {
|
||||
/** NOTE: createServer returns the current server reference unless one is not already created */
|
||||
NimBLEDevice::createServer()->disconnect(desc->conn_handle);
|
||||
if(!connInfo.isEncrypted()) {
|
||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||
printf("Encrypt connection failed - disconnecting client\n");
|
||||
return;
|
||||
}
|
||||
|
@ -75,25 +71,24 @@ class ServerCallbacks: public NimBLEServerCallbacks {
|
|||
|
||||
/** Handler class for characteristic actions */
|
||||
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
|
||||
void onRead(NimBLECharacteristic* pCharacteristic){
|
||||
void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
||||
printf("%s : onRead(), value: %s\n",
|
||||
pCharacteristic->getUUID().toString().c_str(),
|
||||
pCharacteristic->getValue().c_str());
|
||||
};
|
||||
}
|
||||
|
||||
void onWrite(NimBLECharacteristic* pCharacteristic) {
|
||||
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
||||
printf("%s : onWrite(), value: %s\n",
|
||||
pCharacteristic->getUUID().toString().c_str(),
|
||||
pCharacteristic->getValue().c_str());
|
||||
};
|
||||
}
|
||||
|
||||
/** Called before notification or indication is sent,
|
||||
* the value can be changed here before sending if desired.
|
||||
*/
|
||||
void onNotify(NimBLECharacteristic* pCharacteristic) {
|
||||
printf("Sending notification to clients\n");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The value returned in code is the NimBLE host return code.
|
||||
|
@ -101,7 +96,7 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
|
|||
void onStatus(NimBLECharacteristic* pCharacteristic, int code) {
|
||||
printf("Notification/Indication return code: %d, %s\n",
|
||||
code, NimBLEUtils::returnCodeToString(code));
|
||||
};
|
||||
}
|
||||
|
||||
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) {
|
||||
std::string str = "Client ID: ";
|
||||
|
@ -120,17 +115,17 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
|
|||
str += std::string(pCharacteristic->getUUID());
|
||||
|
||||
printf("%s\n", str.c_str());
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/** Handler class for descriptor actions */
|
||||
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
|
||||
void onWrite(NimBLEDescriptor* pDescriptor) {
|
||||
void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||
std::string dscVal = pDescriptor->getValue();
|
||||
printf("Descriptor witten value: %s\n", dscVal.c_str());
|
||||
};
|
||||
|
||||
void onRead(NimBLEDescriptor* pDescriptor) {
|
||||
void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
|
||||
};;
|
||||
};
|
||||
|
|
|
@ -37,11 +37,11 @@ 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 onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
||||
printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str());
|
||||
};
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer) {
|
||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
||||
printf("Client disconnected - sleeping for %u seconds\n", sleepSeconds);
|
||||
esp_deep_sleep_start();
|
||||
};
|
||||
|
|
|
@ -37,11 +37,11 @@ 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 onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
||||
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
|
||||
};
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer) {
|
||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
||||
printf("Client disconnected\n");
|
||||
// if still advertising we won't sleep yet.
|
||||
if (!pServer->getAdvertising()->isAdvertising()) {
|
||||
|
|
|
@ -60,7 +60,7 @@ class MyClientCallback : public BLEClientCallbacks {
|
|||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
}
|
||||
/*******************************************************************/
|
||||
|
|
|
@ -48,11 +48,11 @@ uint32_t value = 0;
|
|||
/** None of these are required as they will be handled by the library with defaults. **
|
||||
** Remove as you see fit for your needs */
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
|
@ -67,7 +67,7 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
|||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
}
|
||||
/*******************************************************************/
|
||||
|
|
|
@ -50,11 +50,11 @@ uint8_t txValue = 0;
|
|||
/** None of these are required as they will be handled by the library with defaults. **
|
||||
** Remove as you see fit for your needs */
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
|
@ -69,14 +69,14 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
|||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
void onWrite(BLECharacteristic *pCharacteristic, BLEConnInfo& connInfo) {
|
||||
std::string rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
|
|
|
@ -263,7 +263,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
{
|
||||
const ble_uuid_t *uuid;
|
||||
int rc;
|
||||
struct ble_gap_conn_desc desc;
|
||||
NimBLEConnInfo peerInfo;
|
||||
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
|
||||
|
@ -273,15 +273,14 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
|
||||
switch(ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR: {
|
||||
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
|
||||
// If the packet header is only 8 bytes this is a follow up of a long read
|
||||
// so we don't want to call the onRead() callback again.
|
||||
if(ctxt->om->om_pkthdr_len > 8 ||
|
||||
pCharacteristic->m_value.size() <= (ble_att_mtu(desc.conn_handle) - 3)) {
|
||||
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
|
||||
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
|
||||
pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) {
|
||||
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo);
|
||||
}
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
|
@ -311,11 +310,10 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
len += next->om_len;
|
||||
next = SLIST_NEXT(next, om_next);
|
||||
}
|
||||
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
pCharacteristic->setValue(buf, len);
|
||||
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
|
||||
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
|
||||
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
|
@ -341,8 +339,8 @@ size_t NimBLECharacteristic::getSubscribedCount() {
|
|||
* This will maintain a vector of subscribed clients and their indicate/notify status.
|
||||
*/
|
||||
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
||||
ble_gap_conn_desc desc;
|
||||
if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
|
||||
NimBLEConnInfo peerInfo;
|
||||
if(ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -379,7 +377,7 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
|||
m_subscribedVec.erase(it);
|
||||
}
|
||||
|
||||
m_pCallbacks->onSubscribe(this, &desc, subVal);
|
||||
m_pCallbacks->onSubscribe(this, peerInfo, subVal);
|
||||
}
|
||||
|
||||
|
||||
|
@ -529,6 +527,7 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
|
|||
}
|
||||
} // setCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the callback handlers for this characteristic.
|
||||
*/
|
||||
|
@ -584,42 +583,25 @@ std::string NimBLECharacteristic::toString() {
|
|||
} // toString
|
||||
|
||||
|
||||
NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||
*/
|
||||
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) {
|
||||
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
|
||||
} // onRead
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
* @param [in] desc The connection description struct that is associated with the peer that performed the read.
|
||||
*/
|
||||
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
|
||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
|
||||
} // onRead
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||
*/
|
||||
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) {
|
||||
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
|
||||
} // onWrite
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
* @param [in] desc The connection description struct that is associated with the peer that performed the write.
|
||||
*/
|
||||
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
|
||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
|
||||
} // onWrite
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a Notify request.
|
||||
|
@ -645,7 +627,7 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
|
|||
/**
|
||||
* @brief Callback function called when a client changes subscription status.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
* @param [in] desc The connection description struct that is associated with the client.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||
* @param [in] subValue The subscription status:
|
||||
* * 0 = Un-Subscribed
|
||||
* * 1 = Notifications
|
||||
|
@ -653,7 +635,7 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
|
|||
* * 3 = Notifications and Indications
|
||||
*/
|
||||
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
|
||||
ble_gap_conn_desc* desc,
|
||||
NimBLEConnInfo& connInfo,
|
||||
uint16_t subValue)
|
||||
{
|
||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef enum {
|
|||
#include "NimBLEService.h"
|
||||
#include "NimBLEDescriptor.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
#include "NimBLEConnInfo.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -200,14 +201,12 @@ private:
|
|||
*/
|
||||
class NimBLECharacteristicCallbacks {
|
||||
public:
|
||||
virtual ~NimBLECharacteristicCallbacks();
|
||||
virtual void onRead(NimBLECharacteristic* pCharacteristic);
|
||||
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
|
||||
virtual void onWrite(NimBLECharacteristic* pCharacteristic);
|
||||
virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
|
||||
virtual ~NimBLECharacteristicCallbacks(){}
|
||||
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
|
||||
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
|
||||
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
|
||||
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
|
|
|
@ -926,7 +926,7 @@ uint16_t NimBLEClient::getMTU() {
|
|||
*/
|
||||
/*STATIC*/
|
||||
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
NimBLEClient* client = (NimBLEClient*)arg;
|
||||
NimBLEClient* pClient = (NimBLEClient*)arg;
|
||||
int rc;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type));
|
||||
|
@ -947,40 +947,40 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
break;
|
||||
default:
|
||||
// Check that the event is for this client.
|
||||
if(client->m_conn_id != event->disconnect.conn.conn_handle) {
|
||||
if(pClient->m_conn_id != event->disconnect.conn.conn_handle) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop the disconnect timer since we are now disconnected.
|
||||
ble_npl_callout_stop(&client->m_dcTimer);
|
||||
ble_npl_callout_stop(&pClient->m_dcTimer);
|
||||
|
||||
// Remove the device from ignore list so we will scan it again
|
||||
NimBLEDevice::removeIgnored(client->m_peerAddress);
|
||||
NimBLEDevice::removeIgnored(pClient->m_peerAddress);
|
||||
|
||||
// No longer connected, clear the connection ID.
|
||||
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
pClient->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
// If we received a connected event but did not get established (no PDU)
|
||||
// then a disconnect event will be sent but we should not send it to the
|
||||
// app for processing. Instead we will ensure the task is released
|
||||
// and report the error.
|
||||
if(!client->m_connEstablished)
|
||||
if(!pClient->m_connEstablished)
|
||||
break;
|
||||
|
||||
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
|
||||
client->m_connEstablished = false;
|
||||
client->m_pClientCallbacks->onDisconnect(client, rc);
|
||||
pClient->m_connEstablished = false;
|
||||
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
|
||||
break;
|
||||
} // BLE_GAP_EVENT_DISCONNECT
|
||||
|
||||
case BLE_GAP_EVENT_CONNECT: {
|
||||
// If we aren't waiting for this connection response
|
||||
// we should drop the connection immediately.
|
||||
if(client->isConnected() || client->m_pTaskData == nullptr) {
|
||||
if(pClient->isConnected() || pClient->m_pTaskData == nullptr) {
|
||||
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||
return 0;
|
||||
}
|
||||
|
@ -989,20 +989,20 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
if (rc == 0) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Connected event");
|
||||
|
||||
client->m_conn_id = event->connect.conn_handle;
|
||||
pClient->m_conn_id = event->connect.conn_handle;
|
||||
|
||||
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
|
||||
rc = ble_gattc_exchange_mtu(pClient->m_conn_id, NULL,NULL);
|
||||
if(rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
|
||||
// In the case of a multiconnecting device we ignore this device when
|
||||
// In the case of a multi-connecting device we ignore this device when
|
||||
// scanning since we are already connected to it
|
||||
NimBLEDevice::addIgnored(client->m_peerAddress);
|
||||
NimBLEDevice::addIgnored(pClient->m_peerAddress);
|
||||
} else {
|
||||
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
pClient->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1010,19 +1010,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} // BLE_GAP_EVENT_CONNECT
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_RX: {
|
||||
if(client->m_conn_id != event->notify_rx.conn_handle)
|
||||
if(pClient->m_conn_id != event->notify_rx.conn_handle)
|
||||
return 0;
|
||||
|
||||
// If a notification comes before this flag is set we might
|
||||
// access a vector while it is being cleared in connect()
|
||||
if(!client->m_connEstablished) {
|
||||
if(!pClient->m_connEstablished) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
|
||||
NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d",
|
||||
event->notify_rx.attr_handle);
|
||||
|
||||
for(auto &it: client->m_servicesVector) {
|
||||
for(auto &it: pClient->m_servicesVector) {
|
||||
// Dont waste cycles searching services without this handle in its range
|
||||
if(it->getEndHandle() < event->notify_rx.attr_handle) {
|
||||
continue;
|
||||
|
@ -1061,7 +1061,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
|
||||
case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: {
|
||||
if(client->m_conn_id != event->conn_update_req.conn_handle){
|
||||
if(pClient->m_conn_id != event->conn_update_req.conn_handle){
|
||||
return 0;
|
||||
}
|
||||
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
|
||||
|
@ -1071,15 +1071,15 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
event->conn_update_req.peer_params->latency,
|
||||
event->conn_update_req.peer_params->supervision_timeout);
|
||||
|
||||
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
|
||||
rc = pClient->m_pClientCallbacks->onConnParamsUpdateRequest(pClient,
|
||||
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
|
||||
|
||||
|
||||
if(!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ ) {
|
||||
event->conn_update_req.self_params->itvl_min = client->m_pConnParams.itvl_min;
|
||||
event->conn_update_req.self_params->itvl_max = client->m_pConnParams.itvl_max;
|
||||
event->conn_update_req.self_params->latency = client->m_pConnParams.latency;
|
||||
event->conn_update_req.self_params->supervision_timeout = client->m_pConnParams.supervision_timeout;
|
||||
event->conn_update_req.self_params->itvl_min = pClient->m_pConnParams.itvl_min;
|
||||
event->conn_update_req.self_params->itvl_max = pClient->m_pConnParams.itvl_max;
|
||||
event->conn_update_req.self_params->latency = pClient->m_pConnParams.latency;
|
||||
event->conn_update_req.self_params->supervision_timeout = pClient->m_pConnParams.supervision_timeout;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "%s peer params", (rc == 0) ? "Accepted" : "Rejected");
|
||||
|
@ -1087,7 +1087,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE: {
|
||||
if(client->m_conn_id != event->conn_update.conn_handle){
|
||||
if(pClient->m_conn_id != event->conn_update.conn_handle){
|
||||
return 0;
|
||||
}
|
||||
if(event->conn_update.status == 0) {
|
||||
|
@ -1099,22 +1099,22 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} // BLE_GAP_EVENT_CONN_UPDATE
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE: {
|
||||
if(client->m_conn_id != event->enc_change.conn_handle){
|
||||
if(pClient->m_conn_id != event->enc_change.conn_handle){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(event->enc_change.status == 0 ||
|
||||
event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING))
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
NimBLEConnInfo peerInfo;
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
|
||||
if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
|
||||
// Key is missing, try deleting.
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
ble_store_util_delete_peer(&peerInfo.m_desc.peer_id_addr);
|
||||
} else {
|
||||
client->m_pClientCallbacks->onAuthenticationComplete(&desc);
|
||||
pClient->m_pClientCallbacks->onAuthenticationComplete(peerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1123,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} //BLE_GAP_EVENT_ENC_CHANGE
|
||||
|
||||
case BLE_GAP_EVENT_MTU: {
|
||||
if(client->m_conn_id != event->mtu.conn_handle){
|
||||
if(pClient->m_conn_id != event->mtu.conn_handle){
|
||||
return 0;
|
||||
}
|
||||
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
|
||||
|
@ -1136,7 +1136,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
||||
struct ble_sm_io pkey = {0,0};
|
||||
|
||||
if(client->m_conn_id != event->passkey.conn_handle)
|
||||
if(pClient->m_conn_id != event->passkey.conn_handle)
|
||||
return 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
|
@ -1148,7 +1148,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
pkey.numcmp_accept = pClient->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
|
||||
|
@ -1166,7 +1166,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest();
|
||||
pkey.passkey = pClient->m_pClientCallbacks->onPassKeyRequest();
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
|
||||
|
@ -1183,12 +1183,12 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
}
|
||||
} // Switch
|
||||
|
||||
if(client->m_pTaskData != nullptr) {
|
||||
client->m_pTaskData->rc = rc;
|
||||
if(client->m_pTaskData->task) {
|
||||
xTaskNotifyGive(client->m_pTaskData->task);
|
||||
if(pClient->m_pTaskData != nullptr) {
|
||||
pClient->m_pTaskData->rc = rc;
|
||||
if(pClient->m_pTaskData->task) {
|
||||
xTaskNotifyGive(pClient->m_pTaskData->task);
|
||||
}
|
||||
client->m_pTaskData = nullptr;
|
||||
pClient->m_pTaskData = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1262,7 +1262,7 @@ uint32_t NimBLEClientCallbacks::onPassKeyRequest(){
|
|||
return 123456;
|
||||
}
|
||||
|
||||
void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){
|
||||
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& peerInfo){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
|
||||
}
|
||||
bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){
|
||||
|
|
|
@ -152,7 +152,7 @@ public:
|
|||
* @param [in] desc A pointer to the struct containing the connection information.\n
|
||||
* This can be used to check the status of the connection encryption/pairing.
|
||||
*/
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when using numeric comparision for pairing.
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
class NimBLEConnInfo {
|
||||
friend class NimBLEServer;
|
||||
friend class NimBLEClient;
|
||||
friend class NimBLECharacteristic;
|
||||
friend class NimBLEDescriptor;
|
||||
|
||||
ble_gap_conn_desc m_desc;
|
||||
NimBLEConnInfo() { m_desc = {}; }
|
||||
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
|
||||
|
|
|
@ -155,7 +155,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
|
||||
const ble_uuid_t *uuid;
|
||||
int rc;
|
||||
struct ble_gap_conn_desc desc;
|
||||
NimBLEConnInfo peerInfo;
|
||||
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
|
||||
|
@ -165,14 +165,14 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
|
||||
switch(ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_DSC: {
|
||||
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
|
||||
// If the packet header is only 8 bytes this is a follow up of a long read
|
||||
// so we don't want to call the onRead() callback again.
|
||||
if(ctxt->om->om_pkthdr_len > 8 ||
|
||||
pDescriptor->m_value.size() <= (ble_att_mtu(desc.conn_handle) - 3)) {
|
||||
pDescriptor->m_pCallbacks->onRead(pDescriptor);
|
||||
pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) {
|
||||
pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo);
|
||||
}
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
|
@ -182,6 +182,9 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
}
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
|
||||
uint16_t att_max_len = pDescriptor->m_value.max_size();
|
||||
|
||||
if (ctxt->om->om_len > att_max_len) {
|
||||
|
@ -203,7 +206,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
}
|
||||
|
||||
pDescriptor->setValue(buf, len);
|
||||
pDescriptor->m_pCallbacks->onWrite(pDescriptor);
|
||||
pDescriptor->m_pCallbacks->onWrite(pDescriptor, peerInfo);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
|
@ -280,13 +283,11 @@ std::string NimBLEDescriptor::toString() {
|
|||
} // toString
|
||||
|
||||
|
||||
NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {}
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) {
|
||||
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||
(void)pDescriptor;
|
||||
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
|
||||
} // onRead
|
||||
|
@ -296,7 +297,7 @@ void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) {
|
|||
* @brief Callback function to support a write request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) {
|
||||
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||
(void)pDescriptor;
|
||||
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
|
||||
} // onWrite
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "NimBLECharacteristic.h"
|
||||
#include "NimBLEUUID.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
#include "NimBLEConnInfo.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -108,9 +109,9 @@ private:
|
|||
*/
|
||||
class NimBLEDescriptorCallbacks {
|
||||
public:
|
||||
virtual ~NimBLEDescriptorCallbacks();
|
||||
virtual void onRead(NimBLEDescriptor* pDescriptor);
|
||||
virtual void onWrite(NimBLEDescriptor* pDescriptor);
|
||||
virtual ~NimBLEDescriptorCallbacks(){}
|
||||
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||
};
|
||||
|
||||
#include "NimBLE2904.h"
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#define BLEBeacon NimBLEBeacon
|
||||
#define BLEEddystoneTLM NimBLEEddystoneTLM
|
||||
#define BLEEddystoneURL NimBLEEddystoneURL
|
||||
#define BLEConnInfo NimBLEConnInfo
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
|
||||
#define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS
|
||||
|
|
|
@ -339,11 +339,11 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
|
|||
*/
|
||||
/*STATIC*/
|
||||
int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
NimBLEServer* server = NimBLEDevice::getServer();
|
||||
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
|
||||
NimBLEUtils::gapEventToString(event->type));
|
||||
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type));
|
||||
|
||||
int rc = 0;
|
||||
struct ble_gap_conn_desc desc;
|
||||
NimBLEConnInfo peerInfo;
|
||||
NimBLEServer* pServer = NimBLEDevice::getServer();
|
||||
|
||||
switch(event->type) {
|
||||
|
||||
|
@ -356,15 +356,14 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
#endif
|
||||
}
|
||||
else {
|
||||
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
|
||||
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
|
||||
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
server->m_pServerCallbacks->onConnect(server);
|
||||
server->m_pServerCallbacks->onConnect(server, &desc);
|
||||
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -386,21 +385,21 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
break;
|
||||
}
|
||||
|
||||
server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(),
|
||||
server->m_connectedPeersVec.end(),
|
||||
pServer->m_connectedPeersVec.erase(std::remove(pServer->m_connectedPeersVec.begin(),
|
||||
pServer->m_connectedPeersVec.end(),
|
||||
event->disconnect.conn.conn_handle),
|
||||
server->m_connectedPeersVec.end());
|
||||
pServer->m_connectedPeersVec.end());
|
||||
|
||||
if(server->m_svcChanged) {
|
||||
server->resetGATT();
|
||||
if(pServer->m_svcChanged) {
|
||||
pServer->resetGATT();
|
||||
}
|
||||
|
||||
server->m_pServerCallbacks->onDisconnect(server);
|
||||
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
|
||||
NimBLEConnInfo peerInfo(event->disconnect.conn);
|
||||
pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason);
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
||||
if(server->m_advertiseOnDisconnect) {
|
||||
server->startAdvertising();
|
||||
if(pServer->m_advertiseOnDisconnect) {
|
||||
pServer->startAdvertising();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -411,18 +410,18 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
event->subscribe.attr_handle,
|
||||
(event->subscribe.cur_notify ? "true":"false"));
|
||||
|
||||
for(auto &it : server->m_notifyChrVec) {
|
||||
for(auto &it : pServer->m_notifyChrVec) {
|
||||
if(it->getHandle() == event->subscribe.attr_handle) {
|
||||
if((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) ||
|
||||
(it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
|
||||
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC))
|
||||
{
|
||||
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!desc.sec_state.encrypted) {
|
||||
if(!peerInfo.isEncrypted()) {
|
||||
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
|
||||
}
|
||||
}
|
||||
|
@ -439,19 +438,20 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.value);
|
||||
rc = ble_gap_conn_find(event->mtu.conn_handle, &desc);
|
||||
|
||||
rc = ble_gap_conn_find(event->mtu.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
server->m_pServerCallbacks->onMTUChange(event->mtu.value, &desc);
|
||||
pServer->m_pServerCallbacks->onMTUChange(event->mtu.value, peerInfo);
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_MTU
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX: {
|
||||
NimBLECharacteristic *pChar = nullptr;
|
||||
|
||||
for(auto &it : server->m_notifyChrVec) {
|
||||
for(auto &it : pServer->m_notifyChrVec) {
|
||||
if(it->getHandle() == event->notify_tx.attr_handle) {
|
||||
pChar = it;
|
||||
}
|
||||
|
@ -465,7 +465,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
if(event->notify_tx.status == 0) {
|
||||
return 0; // Indication sent but not yet acknowledged.
|
||||
}
|
||||
server->clearIndicateWait(event->notify_tx.conn_handle);
|
||||
pServer->clearIndicateWait(event->notify_tx.conn_handle);
|
||||
}
|
||||
|
||||
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
|
||||
|
@ -495,12 +495,12 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
*/
|
||||
|
||||
/* Delete the old bond. */
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0){
|
||||
return BLE_GAP_REPEAT_PAIRING_IGNORE;
|
||||
}
|
||||
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
ble_store_util_delete_peer(&peerInfo.m_desc.peer_id_addr);
|
||||
|
||||
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||
* continue with the pairing operation.
|
||||
|
@ -509,12 +509,12 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} // BLE_GAP_EVENT_REPEAT_PAIRING
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE: {
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc);
|
||||
if(rc != 0) {
|
||||
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
server->m_pServerCallbacks->onAuthenticationComplete(&desc);
|
||||
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_ENC_CHANGE
|
||||
|
||||
|
@ -528,7 +528,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
// if the (static)passkey is the default, check the callback for custom value
|
||||
// both values default to the same.
|
||||
if(pkey.passkey == 123456) {
|
||||
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
|
||||
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
|
||||
|
@ -536,7 +536,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
pkey.numcmp_accept = pServer->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
||||
|
@ -554,7 +554,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
|
||||
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
||||
|
@ -838,49 +838,31 @@ void NimBLEServer::clearIndicateWait(uint16_t conn_handle) {
|
|||
|
||||
|
||||
/** Default callback handlers */
|
||||
|
||||
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
|
||||
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
||||
} // onConnect
|
||||
|
||||
|
||||
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
||||
} // onConnect
|
||||
|
||||
|
||||
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
|
||||
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer,
|
||||
NimBLEConnInfo& connInfo, int reason) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
||||
} // onDisconnect
|
||||
|
||||
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
||||
} // onDisconnect
|
||||
|
||||
void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
|
||||
void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default");
|
||||
} // onMTUChange
|
||||
|
||||
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
|
||||
return 123456;
|
||||
}
|
||||
/*
|
||||
void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key);
|
||||
}
|
||||
} //onPassKeyRequest
|
||||
|
||||
bool NimBLEServerCallbacks::onSecurityRequest(){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true");
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){
|
||||
void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
||||
}
|
||||
} // onAuthenticationComplete
|
||||
|
||||
bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
||||
return true;
|
||||
}
|
||||
} // onConfirmPIN
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
|
|
|
@ -127,41 +127,28 @@ public:
|
|||
* @brief Handle a client connection.
|
||||
* This is called when a client connects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client connection.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onConnect(NimBLEServer* pServer);
|
||||
|
||||
/**
|
||||
* @brief Handle a client connection.
|
||||
* This is called when a client connects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client connection.
|
||||
* @param [in] desc A pointer to the connection description structure containig information
|
||||
* about the connection parameters.
|
||||
*/
|
||||
virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
|
||||
|
||||
/**
|
||||
* @brief Handle a client disconnection.
|
||||
* This is called when a client disconnects.
|
||||
* @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
|
||||
*/
|
||||
virtual void onDisconnect(NimBLEServer* pServer);
|
||||
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Handle a client disconnection.
|
||||
* This is called when a client discconnects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client disconnection.
|
||||
* @param [in] desc A pointer to the connection description structure containing information
|
||||
* about the connection.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* about the peer connection parameters.
|
||||
* @param [in] reason The reason code for the disconnection.
|
||||
*/
|
||||
virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
|
||||
virtual void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason);
|
||||
|
||||
/**
|
||||
* @brief Called when the connection MTU changes.
|
||||
* @param [in] MTU The new MTU value.
|
||||
* @param [in] desc A pointer to the connection description structure containing information
|
||||
* about the connection.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc);
|
||||
virtual void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when a client requests a passkey for pairing.
|
||||
|
@ -169,15 +156,12 @@ public:
|
|||
*/
|
||||
virtual uint32_t onPassKeyRequest();
|
||||
|
||||
//virtual void onPassKeyNotify(uint32_t pass_key);
|
||||
//virtual bool onSecurityRequest();
|
||||
|
||||
/**
|
||||
* @brief Called when the pairing procedure is complete.
|
||||
* @param [in] desc A pointer to the struct containing the connection information.\n
|
||||
* This can be used to check the status of the connection encryption/pairing.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when using numeric comparision for pairing.
|
||||
|
|
Loading…
Reference in a new issue