mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2024-12-22 19:10:46 +01:00
218 lines
9.1 KiB
C++
218 lines
9.1 KiB
C++
|
|
/**
|
|
* NimBLE_Server Demo:
|
|
*
|
|
* Demonstrates many of the available features of the NimBLE server library.
|
|
*
|
|
* Created: on March 22 2020
|
|
* Author: H2zero
|
|
*/
|
|
|
|
#include <NimBLEDevice.h>
|
|
|
|
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, NimBLEConnInfo& connInfo) override {
|
|
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.
|
|
* Units; Min/Max Intervals: 1.25 millisecond increments.
|
|
* Latency: number of intervals allowed to skip.
|
|
* Timeout: 10 millisecond increments.
|
|
*/
|
|
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 18);
|
|
}
|
|
|
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
|
printf("Client disconnected - start advertising\n");
|
|
NimBLEDevice::startAdvertising();
|
|
}
|
|
|
|
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) override {
|
|
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
|
|
}
|
|
|
|
/********************* Security handled here *********************/
|
|
uint32_t onPassKeyDisplay() override {
|
|
printf("Server Passkey Display\n");
|
|
/**
|
|
* This should return a random 6 digit number for security
|
|
* or make your own static passkey as done here.
|
|
*/
|
|
return 123456;
|
|
}
|
|
|
|
void onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
|
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
|
/** Inject false if passkeys don't match. */
|
|
NimBLEDevice::injectConfirmPasskey(connInfo, true);
|
|
}
|
|
|
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
|
|
/** Check that encryption was successful, if not we disconnect the client */
|
|
if (!connInfo.isEncrypted()) {
|
|
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
|
printf("Encrypt connection failed - disconnecting client\n");
|
|
return;
|
|
}
|
|
|
|
printf("Secured connection to: %s\n", connInfo.getAddress().toString().c_str());
|
|
}
|
|
} serverCallbacks;
|
|
|
|
/** Handler class for characteristic actions */
|
|
class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
|
void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
|
|
printf("%s : onRead(), value: %s\n",
|
|
pCharacteristic->getUUID().toString().c_str(),
|
|
pCharacteristic->getValue().c_str());
|
|
}
|
|
|
|
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
|
|
printf("%s : onWrite(), value: %s\n",
|
|
pCharacteristic->getUUID().toString().c_str(),
|
|
pCharacteristic->getValue().c_str());
|
|
}
|
|
|
|
/**
|
|
* The value returned in code is the NimBLE host return code.
|
|
*/
|
|
void onStatus(NimBLECharacteristic* pCharacteristic, int code) override {
|
|
printf("Notification/Indication return code: %d, %s\n", code, NimBLEUtils::returnCodeToString(code));
|
|
}
|
|
|
|
/** Peer subscribed to notifications/indications */
|
|
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override {
|
|
std::string str = "Client ID: ";
|
|
str += connInfo.getConnHandle();
|
|
str += " Address: ";
|
|
str += connInfo.getAddress().toString();
|
|
if (subValue == 0) {
|
|
str += " Unsubscribed to ";
|
|
} else if (subValue == 1) {
|
|
str += " Subscribed to notifications for ";
|
|
} else if (subValue == 2) {
|
|
str += " Subscribed to indications for ";
|
|
} else if (subValue == 3) {
|
|
str += " Subscribed to notifications and indications for ";
|
|
}
|
|
str += std::string(pCharacteristic->getUUID());
|
|
|
|
printf("%s\n", str.c_str());
|
|
}
|
|
} chrCallbacks;
|
|
|
|
/** Handler class for descriptor actions */
|
|
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
|
|
void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
|
|
std::string dscVal = pDescriptor->getValue();
|
|
printf("Descriptor written value: %s\n", dscVal.c_str());
|
|
}
|
|
|
|
void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
|
|
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
|
|
}
|
|
} dscCallbacks;
|
|
|
|
extern "C" void app_main(void) {
|
|
printf("Starting NimBLE Server\n");
|
|
|
|
/** Initialize NimBLE and set the device name */
|
|
NimBLEDevice::init("NimBLE");
|
|
|
|
/**
|
|
* Set the IO capabilities of the device, each option will trigger a different pairing method.
|
|
* BLE_HS_IO_DISPLAY_ONLY - Passkey pairing
|
|
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
|
|
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
|
|
*/
|
|
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey
|
|
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
|
|
|
|
/**
|
|
* 2 different ways to set security - both calls achieve the same result.
|
|
* no bonding, no man in the middle protection, BLE secure connections.
|
|
*
|
|
* These are the default values, only shown here for demonstration.
|
|
*/
|
|
// NimBLEDevice::setSecurityAuth(false, false, true);
|
|
|
|
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
|
|
pServer = NimBLEDevice::createServer();
|
|
pServer->setCallbacks(&serverCallbacks);
|
|
|
|
NimBLEService* pDeadService = pServer->createService("DEAD");
|
|
NimBLECharacteristic* pBeefCharacteristic =
|
|
pDeadService->createCharacteristic("BEEF",
|
|
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE |
|
|
/** Require a secure connection for read and write access */
|
|
NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted
|
|
NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted
|
|
);
|
|
|
|
pBeefCharacteristic->setValue("Burger");
|
|
pBeefCharacteristic->setCallbacks(&chrCallbacks);
|
|
|
|
/**
|
|
* 2902 and 2904 descriptors are a special case, when createDescriptor is called with
|
|
* either of those uuid's it will create the associated class with the correct properties
|
|
* and sizes. However we must cast the returned reference to the correct type as the method
|
|
* only returns a pointer to the base NimBLEDescriptor class.
|
|
*/
|
|
NimBLE2904* pBeef2904 = pBeefCharacteristic->create2904();
|
|
pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8);
|
|
pBeef2904->setCallbacks(&dscCallbacks);
|
|
|
|
NimBLEService* pBaadService = pServer->createService("BAAD");
|
|
NimBLECharacteristic* pFoodCharacteristic =
|
|
pBaadService->createCharacteristic("F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
|
|
|
|
pFoodCharacteristic->setValue("Fries");
|
|
pFoodCharacteristic->setCallbacks(&chrCallbacks);
|
|
|
|
/** Custom descriptor: Arguments are UUID, Properties, max length of the value in bytes */
|
|
NimBLEDescriptor* pC01Ddsc =
|
|
pFoodCharacteristic->createDescriptor("C01D",
|
|
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC,
|
|
20);
|
|
pC01Ddsc->setValue("Send it back!");
|
|
pC01Ddsc->setCallbacks(&dscCallbacks);
|
|
|
|
/** Start the services when finished creating all Characteristics and Descriptors */
|
|
pDeadService->start();
|
|
pBaadService->start();
|
|
|
|
/** Create an advertising instance and add the services to the advertised data */
|
|
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
|
pAdvertising->setName("NimBLE-Server");
|
|
pAdvertising->addServiceUUID(pDeadService->getUUID());
|
|
pAdvertising->addServiceUUID(pBaadService->getUUID());
|
|
/**
|
|
* If your device is battery powered you may consider setting scan response
|
|
* to false as it will extend battery life at the expense of less data sent.
|
|
*/
|
|
pAdvertising->enableScanResponse(true);
|
|
pAdvertising->start();
|
|
|
|
printf("Advertising Started\n");
|
|
|
|
/** Loop here and send notifications to connected peers */
|
|
for (;;) {
|
|
if (pServer->getConnectedCount()) {
|
|
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
|
|
if (pSvc) {
|
|
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
|
|
if (pChr) {
|
|
pChr->notify();
|
|
}
|
|
}
|
|
}
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|