esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp
2020-08-22 09:56:19 -06:00

226 lines
9.5 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"
#include "NimBLELog.h"
#include <stdio.h>
extern "C" {void app_main(void);}
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());
/** 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, try for 3x interval time for best results.
*/
pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 18);
};
void onDisconnect(NimBLEServer* pServer) {
printf("Client disconnected - start advertising\n");
NimBLEDevice::startAdvertising();
};
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
printf("Server Passkey Request\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
bool onConfirmPIN(uint32_t pass_key){
printf("The passkey YES/NO number: %d\n", pass_key);
/** Return false if passkeys don't match. */
return true;
};
void onAuthenticationComplete(ble_gap_conn_desc* desc){
/** 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);
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
};
/** Handler class for characteristic actions */
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
void onRead(NimBLECharacteristic* pCharacteristic){
printf("%s : onRead(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str());
};
void onWrite(NimBLECharacteristic* pCharacteristic) {
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 status returned in status is defined in NimBLECharacteristic.h.
* The value returned in code is the NimBLE host return code.
*/
void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) {
printf("Notification/Indication status code: %d , return code: %d, %s\n",
status,
code,
NimBLEUtils::returnCodeToString(code));
};
};
/** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor) {
std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
printf("Descriptor witten value: %s\n", dscVal.c_str());
};
void onRead(NimBLEDescriptor* pDescriptor) {
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
};;
};
/** Define callback instances globally to use for multiple Charateristics \ Descriptors */
static DescriptorCallbacks dscCallbacks;
static CharacteristicCallbacks chrCallbacks;
void notifyTask(void * parameter){
for(;;) {
if(pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if(pChr) {
pChr->notify(true);
}
}
}
vTaskDelay(2000/portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main(void) {
printf("Starting NimBLE Server\n");
/** sets 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, 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(new 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 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904");
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 in bytes of the value */
NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor(
"C01D",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE|
NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted
20
);
pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pDeadService->start();
pBaadService->start();
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/** Add the services to the advertisment data **/
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->setScanResponse(true);
pAdvertising->start();
printf("Advertising Started\n");
xTaskCreate(notifyTask, "notifyTask", 5000, NULL, 1, NULL);
}