diff --git a/Kconfig b/Kconfig index d10a5d07..4443b09e 100644 --- a/Kconfig +++ b/Kconfig @@ -1,5 +1,17 @@ menu "ESP-NimBLE-CPP configuration" +config NIMBLE_USING_ARDUINO_IOT + bool "Renames the enums READ and WRITE that conflit with the Arduino IoT lib" + default "n" + help + Enabling this option will change the enum NIMBLE_PROPERTY:READ to NIMBLE_PROPERTY:BLE_READ and NIMBLE_PROPERTY:WRITE to NIMBLE_PROPERTY:BLE_WRITE + +config NIMBLE_USE_MAGIC_ENUM + bool "Enable if magic enum lib is set to get detailed text for Class of device property" + default "n" + help + Enable if magic enum lib is set to get detailed text for Class of device property + choice NIMBLE_CPP_LOG_LEVEL prompt "NimBLE CPP log verbosity" default NIMBLE_CPP_LOG_LEVEL_NONE diff --git a/src/NimBLEAdvertisementData.cpp b/src/NimBLEAdvertisementData.cpp index f45ba3e7..8cbad624 100644 --- a/src/NimBLEAdvertisementData.cpp +++ b/src/NimBLEAdvertisementData.cpp @@ -14,9 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "NimBLEAdvertisementData.h" -#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_) +#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) # include "NimBLEDevice.h" # include "NimBLEUtils.h" @@ -31,6 +30,22 @@ static const char* LOG_TAG = "NimBLEAdvertisementData"; +/** + * @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 + * A flag value of 0 will remove the flags from the advertisement. + */ +bool NimBLEAdvertisementData::setCODData(const NimClassOfDeviceType::bluetooth_cod_t cod) { + int dataLoc = getDataLocation(BLE_HS_ADV_TYPE_CLASS_OF_DEVICE); + if (dataLoc != -1) { + removeData(BLE_HS_ADV_TYPE_CLASS_OF_DEVICE); + } + return addData((const uint8_t*)((NimClassOfDeviceType::makeATT_Payload_CodeClassOfDevice(cod)).data()),5); +} // SetCOD + /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. @@ -38,8 +53,9 @@ static const char* LOG_TAG = "NimBLEAdvertisementData"; */ bool NimBLEAdvertisementData::addData(const uint8_t* data, size_t length) { if (m_payload.size() + length > BLE_HS_ADV_MAX_SZ) { - NIMBLE_LOGE(LOG_TAG, "Data length exceeded"); - return false; + + NIMBLE_LOGE(LOG_TAG, "Data length exceeded %i mx lenth id %i",m_payload.size() + length, BLE_HS_ADV_MAX_SZ); + NIMBLE_LOGE(LOG_TAG, "Current data %s", NimBLEUtils::dataToHexString(m_payload.data(),m_payload.size()).c_str()); } m_payload.insert(m_payload.end(), data, data + length); diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index c2811454..5fa1ee0b 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -64,12 +64,12 @@ class NimBLEAdvertising { void setMaxInterval(uint16_t maxInterval); void setMinInterval(uint16_t minInterval); - bool setAdvertisementData(const NimBLEAdvertisementData& advertisementData); - bool setScanResponseData(const NimBLEAdvertisementData& advertisementData); + bool setAdvertisementData(const NimBLEAdvertisementData& advertisementData); + bool setScanResponseData(const NimBLEAdvertisementData& advertisementData); const NimBLEAdvertisementData& getAdvertisementData(); const NimBLEAdvertisementData& getScanData(); - void clearData(); - bool refreshAdvertisingData(); + void clearData(); + bool refreshAdvertisingData(); bool addServiceUUID(const NimBLEUUID& serviceUUID); bool addServiceUUID(const char* serviceUUID); diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 789d21d7..bcd4e2b4 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -32,6 +32,12 @@ class NimBLE2904; # include # include +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# define WRITE BLE_WRITE +# define READ BLE_READ +# endif + /** * @brief The model of a BLE Characteristic. * @@ -254,5 +260,12 @@ class NimBLECharacteristicCallbacks { virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue); }; + +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# undef WRITE +# undef READ +# endif + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) #endif // NIMBLE_CPP_CHARACTERISTIC_H_ diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index 8f1bb41e..0d765f7c 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -23,6 +23,12 @@ # include +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# define WRITE BLE_WRITE +# define READ BLE_READ +# endif + static const char* LOG_TAG = "NimBLEDescriptor"; static NimBLEDescriptorCallbacks defaultCallbacks; @@ -147,4 +153,11 @@ void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLECon NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); } // onWrite + +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# undef WRITE +# undef READ +# endif + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) diff --git a/src/NimBLEExtAdvertising.cpp b/src/NimBLEExtAdvertising.cpp index d3f78c33..2b565eb9 100644 --- a/src/NimBLEExtAdvertising.cpp +++ b/src/NimBLEExtAdvertising.cpp @@ -616,11 +616,6 @@ bool NimBLEExtAdvertisement::setFlags(uint8_t flag) { * @return True if successful. */ bool NimBLEExtAdvertisement::setManufacturerData(const uint8_t* data, size_t length) { - if (length > 0xFF - 1) { - NIMBLE_LOGE(LOG_TAG, "Manufacturer data too long!"); - return false; - } - uint8_t header[2]; header[0] = length + 1; header[1] = BLE_HS_ADV_TYPE_MFG_DATA; @@ -657,11 +652,6 @@ bool NimBLEExtAdvertisement::setManufacturerData(const std::vector& dat * @return True if successful. */ bool NimBLEExtAdvertisement::setURI(const std::string& uri) { - if (uri.length() > 0xFF - 1) { - NIMBLE_LOGE(LOG_TAG, "URI too long!"); - return false; - } - uint8_t header[2]; header[0] = uri.length() + 1; header[1] = BLE_HS_ADV_TYPE_URI; @@ -680,11 +670,6 @@ bool NimBLEExtAdvertisement::setURI(const std::string& uri) { * @return True if successful. */ bool NimBLEExtAdvertisement::setName(const std::string& name, bool isComplete) { - if (name.length() > 0xFF - 1) { - NIMBLE_LOGE(LOG_TAG, "Name too long!"); - return false; - } - uint8_t header[2]; header[0] = name.length() + 1; header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME; @@ -932,12 +917,8 @@ bool NimBLEExtAdvertisement::setServices(bool complete, uint8_t size, const std: */ bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) { uint8_t uuidBytes = uuid.bitSize() / 8; - if (length + uuidBytes + 2 > 0xFF) { - NIMBLE_LOGE(LOG_TAG, "Service data too long!"); - return false; - } + uint8_t sDataLen = 2 + uuidBytes + length; - uint8_t sDataLen = 2 + uuidBytes + length; if (m_payload.size() + sDataLen > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) { return false; } @@ -1109,4 +1090,20 @@ std::string NimBLEExtAdvertisement::toString() const { return str; } // toString +/** + * @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 + * A flag value of 0 will remove the flags from the advertisement. + */ +bool NimBLEExtAdvertisement::setCODData(const NimClassOfDeviceType::bluetooth_cod_t cod) { + int dataLoc = getDataLocation(BLE_HS_ADV_TYPE_CLASS_OF_DEVICE); + if (dataLoc != -1) { + removeData(BLE_HS_ADV_TYPE_CLASS_OF_DEVICE); + } + return addData((const uint8_t*)((NimClassOfDeviceType::makeATT_Payload_CodeClassOfDevice(cod)).data()),5); +} // SetCOD + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV) diff --git a/src/NimBLEExtAdvertising.h b/src/NimBLEExtAdvertising.h index 4591ce46..185f4619 100644 --- a/src/NimBLEExtAdvertising.h +++ b/src/NimBLEExtAdvertising.h @@ -19,6 +19,7 @@ #define NIMBLE_CPP_EXTADVERTISING_H_ #include "syscfg/syscfg.h" +#include "NimClassOfDeviceType.h" #if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV) # if defined(CONFIG_NIMBLE_CPP_IDF) @@ -46,6 +47,8 @@ class NimBLEUUID; class NimBLEExtAdvertisement { public: NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, uint8_t secPhy = BLE_HCI_LE_PHY_1M); + + bool setCODData(const NimClassOfDeviceType::bluetooth_cod_t cod); bool setAppearance(uint16_t appearance); bool addServiceUUID(const NimBLEUUID& serviceUUID); bool addServiceUUID(const char* serviceUUID); diff --git a/src/NimBLEHIDDevice.cpp b/src/NimBLEHIDDevice.cpp index 105ef11b..44b92715 100644 --- a/src/NimBLEHIDDevice.cpp +++ b/src/NimBLEHIDDevice.cpp @@ -22,6 +22,12 @@ # include "NimBLEService.h" # include "NimBLE2904.h" +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# define WRITE BLE_WRITE +# define READ BLE_READ +# endif + static constexpr uint16_t deviceInfoSvcUuid = 0x180a; static constexpr uint16_t hidSvcUuid = 0x1812; static constexpr uint16_t batterySvcUuid = 0x180f; @@ -340,4 +346,11 @@ NimBLEService* NimBLEHIDDevice::getBatteryService() { return m_batterySvc; } // getBatteryService + +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# undef WRITE +# undef READ +# endif + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) diff --git a/src/NimBLELocalValueAttribute.h b/src/NimBLELocalValueAttribute.h index 56acb729..2f49c272 100644 --- a/src/NimBLELocalValueAttribute.h +++ b/src/NimBLELocalValueAttribute.h @@ -33,11 +33,17 @@ /**************************/ typedef enum { - READ = BLE_GATT_CHR_F_READ, - READ_ENC = BLE_GATT_CHR_F_READ_ENC, - READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, - READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, - WRITE = BLE_GATT_CHR_F_WRITE, +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) + BLE_READ = BLE_GATT_CHR_F_READ, + BLE_WRITE = BLE_GATT_CHR_F_WRITE, +# else + READ = BLE_GATT_CHR_F_READ, + WRITE = BLE_GATT_CHR_F_WRITE, +# endif + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, @@ -101,7 +107,7 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute, public NimBLEValu NimBLELocalValueAttribute(const NimBLEUUID& uuid, uint16_t handle, uint16_t maxLen, - uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)) + uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) : NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {} /** * @brief Destroy the NimBLELocalValueAttribute object. diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 5e4f381b..b43388eb 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -95,6 +95,8 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID& uuid) { m_svcVec.push_back(pService); serviceChanged(); + // added for debugging by synapse2 + NIMBLE_LOGI(LOG_TAG, "Added serice to GATT table service %s", pService->toString().c_str()); return pService; } // createService @@ -189,7 +191,7 @@ void NimBLEServer::start() { return; } -# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4 +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 ble_gatts_show_local(); # endif @@ -742,6 +744,17 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { # endif } // removeService +/** + * @brief debug to get a string of teeh services database + * + */ +std::string NimBLEServer::toString(){ + + std::string rStr ="Nimble Server service count " + m_svcVec.size(); + "\n"; + + return rStr; +} + /** * @brief Adds a service which was either already created but removed from availability,\n * or created and later added to services list. @@ -1032,4 +1045,6 @@ void NimBLEServerCallbacks::onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, NIMBLE_LOGD("NimBLEServerCallbacks", "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy); } // onPhyUpdate + + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 214578ab..8d76c00c 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -34,6 +34,7 @@ # include # include +#include # define NIMBLE_ATT_REMOVE_HIDE 1 # define NIMBLE_ATT_REMOVE_DELETE 2 @@ -84,6 +85,8 @@ class NimBLEServer { bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions); bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy); + std::string toString(); + # if MYNEWT_VAL(BLE_ROLE_CENTRAL) NimBLEClient* getClient(uint16_t connHandle); NimBLEClient* getClient(const NimBLEConnInfo& connInfo); diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 11d7c59c..403580fc 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -21,12 +21,21 @@ #include "syscfg/syscfg.h" #if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) + class NimBLEService; # include "NimBLEAttribute.h" # include "NimBLEServer.h" # include "NimBLECharacteristic.h" + +/// updated as READ and WRITE conflict with ArduinoIOT libs +#if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +#define WRITE BLE_WRITE +#define READ BLE_READ +#endif + + /** * @brief The model of a BLE service. * @@ -69,5 +78,11 @@ class NimBLEService : public NimBLELocalAttribute { ble_gatt_svc_def m_pSvcDef[2]{}; }; // NimBLEService +// updated as READ and WRITE conflict with ArduinoIOT libs +# if defined(CONFIG_NIMBLE_USING_ARDUINO_IOT) +# undef WRITE +# undef READ +# endif + #endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) #endif // NIMBLE_CPP_SERVICE_H_ diff --git a/src/NimClassOfDeviceType.cpp b/src/NimClassOfDeviceType.cpp new file mode 100644 index 00000000..ab039212 --- /dev/null +++ b/src/NimClassOfDeviceType.cpp @@ -0,0 +1,200 @@ + +#include "NimBLELog.h" +#include "NimBLEUtils.h" + + +#if defined(CONFIG_NIMBLE_USE_MAGIC_ENUM) +# include "magicEnum/magic_enum.hpp" +# include "magicEnum/magic_enum_iostream.hpp" + +template +auto to_integer(magic_enum::Enum value) -> int +{ + // magic_enum::Enum - C++17 Concept for enum type. + return static_cast>(value); +} + +#endif +#include "NimClassOfDeviceType.h" + +#define BLE_HS_ADV_TYPE_CLASS_OF_DEV 0x0d + +static const char* LOG_TAG = "NimClassOfDeviceType"; + +namespace NimClassOfDeviceType { + +// fucntion to add the serivce H aand L comps together +static NimClassOfDeviceType::service_class_t getH_LService(bluetooth_cod_t cod) { + uint16_t service_highBit = (cod.bit_field.service_classH << 3); + uint16_t service = service_highBit | ((uint16_t)cod.bit_field.service_classL); + NIMBLE_LOGI(LOG_TAG, + " Merging Service H (0x%02x) and L (0x%02x) into (0x%02x) ", + service_highBit, + cod.bit_field.service_classL, + service); + + return (NimClassOfDeviceType::service_class_t)service; +} + +// Function to construct a CoD from its components +bluetooth_cod_t encodeClassOfDevice(service_class_t services, major_device_class_t major, uint8_t minor) { + bluetooth_cod_t cod = {0x000000}; // Initialize all bits to zero + cod.bit_field.format_type = 0; // Standard format + cod.bit_field.major_device = major & 0x1f; // only use the 5 lower bits + cod.bit_field.minor_device = (minor & 0x7f) ; // only use the higher 6 bits and move for the 2 bits unused + + NIMBLE_LOGI(LOG_TAG, "MINOR in 0x%02x and masked 0x%02x out 0x%02x", minor, (minor & 0x7f),cod.bit_field.minor_device); + + if (services > 0x007) { + // any bigger we get a core dump so we need to manually break the service setting. + uint8_t L_3Bit = services & 0x0007; // only use the 3 lower bits + cod.bit_field.service_classL = L_3Bit; + uint8_t H_8Bit = + (uint8_t)((services >> 3) & + 0x00FF); // get the remaining 8 bits and shift left by 3 for deleting the 3 bits to discard + cod.bytes3.byte2 = H_8Bit; // stuff the reming 8 bits in the byte 2 + NIMBLE_LOGI(LOG_TAG, "Breaking services 0x%02x into 3 bits 0x%02x and 8 bits 0x%02x", services, L_3Bit, H_8Bit); + } else { + // increasing bits from 11 to 16 as the esp32 core dumps at higher bits ( this filed (two bytes)is now across three bytes !) + cod.bit_field.service_classL = services & 0x007; // only use the 3 lower bits + } + + NIMBLE_LOGI(LOG_TAG, + "Making servies %s RAW HEX cod value only three bytes are used %s", + decodeClassOfDevice(cod).c_str(), + NimBLEUtils::dataToHexString((((const unsigned char*)&cod)), 3).c_str()); + + NIMBLE_LOGD(LOG_TAG, + " Making COB Byte0:0x%02x Byte1:0x%02x Byte2:0x%02x", + (cod.bytes3.byte0), + (cod.bytes3.byte1), + (cod.bytes3.byte2)); + return cod; +} + +std::string decodeMajorMinor(major_device_class_t major, uint8_t minor) { + std::string ret = "Magic Enum not enabled"; +#if defined(CONFIG_NIMBLE_USE_MAGIC_ENUM) + std::string cmd = ""; + ret = " Major Device:"; + cmd = ((magic_enum::enum_flags_name(major))); + if (major == 0) { + cmd = "COD_MAJOR_MISCELLANEOUS"; + } + ret = ret + cmd; + ret = ret + " Minor Device:"; + switch (major) { + case major_device_class_t::COD_MAJOR_AUDIO_VIDEO: + cmd = ((magic_enum::enum_flags_name((av_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_AV_UNCATEGORIZED"; + } + break; + case major_device_class_t::COD_MAJOR_COMPUTER: + cmd = ((magic_enum::enum_flags_name((computer_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_COMPUTER_UNCATEGORIZED"; + } + break; + case major_device_class_t::COD_MAJOR_HEALTH: + cmd = ((magic_enum::enum_flags_name((health_minor_class_t)minor))); + + if (minor == 0) { + cmd = "COD_MINOR_HEALTH_UNDEFINED"; + } + break; + case major_device_class_t::COD_MAJOR_IMAGING: + cmd = ((magic_enum::enum_flags_name((imaging_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_IMAGING_UNCATEGORIZED"; + } + break; + + case major_device_class_t::COD_MAJOR_NETWORK: + cmd = ((magic_enum::enum_flags_name((lan_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_LAN_UTIL_FULLY_AVAILABLE"; + } + break; + + case major_device_class_t::COD_MAJOR_PERIPHERAL: + cmd = ((magic_enum::enum_flags_name((peripheral_pointing_device_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_PERIPHERA_UNCATEGORIZED"; + } + break; + case major_device_class_t::COD_MAJOR_PHONE: + cmd = ((magic_enum::enum_flags_name((phone_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_PHONE_UNCATEGORIZED"; + } + break; + case major_device_class_t::COD_MAJOR_TOY: + cmd = ((magic_enum::enum_flags_name((toy_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_TOY_UNCATEGORIZED"; + } + break; + + case major_device_class_t::COD_MAJOR_WEARABLE: + cmd = ((magic_enum::enum_flags_name((wearable_minor_class_t)minor))); + if (minor == 0) { + cmd = "COD_MINOR_WEARABLE_UNCATEGORIZED"; + } + break; + + default: + cmd = "Undefined"; + break; + } + ret = ret + cmd + " "; +#endif + return ret; +} + +std::string decodeClassOfDevice(service_class_t services, major_device_class_t major, uint8_t minor) { + std::string ret = "MAGIC Enum not enabled"; + +#if defined(CONFIG_NIMBLE_USE_MAGIC_ENUM) + ret = "Service Class:"; + std::string cmd = ""; + cmd = magic_enum::enum_flags_name(services); + if (services == 0) { + cmd = "COD_SERVICE_NA"; + } + ret = ret + cmd; + cmd = decodeMajorMinor(major, minor); + ret = ret + cmd; +#endif + + return ret; +} + +std::string decodeClassOfDevice(bluetooth_cod_t cod) { + // NIMBLE_LOGD(LOG_TAG, + // "In DeCODE COS - RAW HEX cod value only 3 bytes are used %s", + // NimBLEUtils::dataToHexString((((const unsigned char*)&cod)), 3).c_str()); + return decodeClassOfDevice(getH_LService(cod), (major_device_class_t)cod.bit_field.major_device, cod.bit_field.minor_device); +} + +std::vector makeATT_Payload_CodeClassOfDevice(bluetooth_cod_t cod) { + uint8_t out[5]; + + // we are folling little edinan format here + out[0] = 4; // length + out[1] = BLE_HS_ADV_TYPE_CLASS_OF_DEV; // class of device + out[2] = (cod.bytes3.byte0); + out[3] = (cod.bytes3.byte1); + out[4] = (cod.bytes3.byte2); + std::vector v(out, out + 5); + + NIMBLE_LOGD(LOG_TAG, " COB Byte0:%02x Byte1:%02x Byte2:%02x", (cod.bytes3.byte0), (cod.bytes3.byte1), (cod.bytes3.byte2)); + NIMBLE_LOGD(LOG_TAG, + " Convetred COB %s to bytes in hex for ADV playload %s", + decodeClassOfDevice(cod).c_str(), + NimBLEUtils::dataToHexString(v.data(), v.size()).c_str()); + + return v; +} + +} // namespace NimClassOfDeviceType \ No newline at end of file diff --git a/src/NimClassOfDeviceType.h b/src/NimClassOfDeviceType.h new file mode 100644 index 00000000..9a29c632 --- /dev/null +++ b/src/NimClassOfDeviceType.h @@ -0,0 +1,261 @@ +#pragma once + +#include +#include +#include +#include +#include + +#if defined(CONFIG_NIMBLE_USE_MAGIC_ENUM) +# include "magicEnum/magic_enum.hpp" +# include "magicEnum/magic_enum_iostream.hpp" +#endif + +#define BLE_HS_ADV_TYPE_CLASS_OF_DEVICE 0x0d + +namespace NimClassOfDeviceType { + +// Use __attribute__((packed)) to prevent the compiler from adding padding, +// which ensures the struct is exactly 24 bits (3 bytes) long. +// but we need to allocate 34 bits as we have service class broken aacross two bytes and we need to use int 16 for it +// note esp 32 is liitle edian. ie byte0 is least significant and byte 2 is most +typedef union { + struct { + uint8_t format_type : 2; // Bits 0-1 (least significant) + uint8_t minor_device : 6; // Bits 2-8 + uint8_t major_device : 5; // Bits 8-12 + uint8_t service_classL : 3; // Bits 13-15 (out of 13 - 23) we have to break the serivce class as it core dumps other wise + uint8_t service_classH : 8; // Bits 16-23 (out of 13 - 23) we have to break the serivce class as it core dumps other wise + } __attribute__((packed)) bit_field; + struct { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + } __attribute__((packed)) bytes3; + +} __attribute__((packed)) bluetooth_cod_t; + +// Enum for Service Class (using a bitmask) +typedef enum : uint16_t { + COD_SERVICE_NA = 0x0000, + COD_SERVICE_LIMITED_DISCOVERABLE = 0x0001, + COD_SERVICE_LE_AUDIO = 0x0002, + COD_SERVICE_RESERVED = 0x0004, + COD_SERVICE_POSITIONING = 0x0008, + COD_SERVICE_NETWORKING = 0x0010, + COD_SERVICE_RENDERING = 0x0020, + COD_SERVICE_CAPTURING = 0x0040, + COD_SERVICE_OBJECT_TRANSFER = 0x0080, + COD_SERVICE_AUDIO = 0x0100, + COD_SERVICE_TELEPHONY = 0x0200, + COD_SERVICE_INFORMATION = 0x0400 +} service_class_t; + +// Enum for Major Device Class +typedef enum : uint8_t { + COD_MAJOR_MISCELLANEOUS = 0x00, + COD_MAJOR_COMPUTER = 0x01, + COD_MAJOR_PHONE = 0x02, + COD_MAJOR_NETWORK = 0x03, + COD_MAJOR_AUDIO_VIDEO = 0x04, + COD_MAJOR_PERIPHERAL = 0x05, + COD_MAJOR_IMAGING = 0x06, + COD_MAJOR_WEARABLE = 0x07, + COD_MAJOR_TOY = 0x8, + COD_MAJOR_HEALTH = 0x09, + COD_MAJOR_UNCATEGORY = 0x1F +} major_device_class_t; + +// Enum for Minor Device Class when Major Class is 'Computer' +typedef enum : uint8_t { + COD_MINOR_COMPUTER_UNCATEGORIZED = 0x00, // Uncategorized computer + COD_MINOR_COMPUTER_DESKTOP = 0x01, // Desktop computer + COD_MINOR_COMPUTER_SERVER = 0x02, // Server-class computer + COD_MINOR_COMPUTER_LAPTOP = 0x03, // Laptop computer + COD_MINOR_COMPUTER_HANDHELD_CLAM = 0x04, // Handheld PC/PDA (clam shell) + COD_MINOR_COMPUTER_PALM_SIZE = 0x05, // Palm-sized PC/PDA + COD_MINOR_COMPUTER_WEARABLE = 0x06, // Wearable computer + COD_MINOR_COMPUTER_TABLET = 0x07 // Tablet computer +} computer_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'Phone' +typedef enum : uint8_t { + COD_MINOR_PHONE_UNCATEGORIZED = 0x00, // Uncategorized phone device + COD_MINOR_PHONE_CELLULAR = 0x01, // A cellular phone + COD_MINOR_PHONE_CORDLESS = 0x02, // A cordless phone + COD_MINOR_PHONE_SMARTPHONE = 0x03, // A smartphone + COD_MINOR_PHONE_WIRED_MODEM = 0x04, // A wired modem or voice gateway + COD_MINOR_PHONE_COMMON_ISDN_ACCESS = 0x05, // A device that provides ISDN access +} phone_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'LAN/Network Access Point' +typedef enum : uint8_t { + COD_MINOR_LAN_UTIL_FULLY_AVAILABLE = 0x00, // 0% utilized + COD_MINOR_LAN_UTIL_1_TO_17_PERC = 0x08, // 1-17% utilized + COD_MINOR_LAN_UTIL_17_TO_33_PERC = 0x10, // 17-33% utilized + COD_MINOR_LAN_UTIL_33_TO_50_PERC = 0x18, // 33-50% utilized + COD_MINOR_LAN_UTIL_50_TO_67_PERC = 0x20, // 50-67% utilized + COD_MINOR_LAN_UTIL_67_TO_83_PERC = 0x21, // 67-83% utilized + COD_MINOR_LAN_UTIL_83_TO_99_PERC = 0x28, // 83-99% utilized + COD_MINOR_LAN_UTIL_NO_SERVICE = 0x38, // No service available (100% utilized) +} lan_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'Audio/Video' +typedef enum : uint8_t { + COD_MINOR_AV_UNCATEGORIZED = 0x00, // Uncategorized, code not assigned + COD_MINOR_AV_HEADSET = 0x01, // Wearable Headset Device + COD_MINOR_AV_HANDS_FREE = 0x02, // Hands-free device + COD_MINOR_AV_MICROPHONE = 0x04, // Microphone + COD_MINOR_AV_LOUDSPEAKER = 0x05, // Loudspeaker + COD_MINOR_AV_HEADPHONES = 0x06, // Headphones + COD_MINOR_AV_PORTABLE_AUDIO = 0x07, // Portable Audio device + COD_MINOR_AV_CAR_AUDIO = 0x08, // Car audio + COD_MINOR_AV_SET_TOP_BOX = 0x09, // Set-top box + COD_MINOR_AV_HIFI_AUDIO = 0x0A, // HiFi Audio device + COD_MINOR_AV_VCR = 0x0B, // VCR + COD_MINOR_AV_VIDEO_CAMERA = 0x0C, // Video Camera + COD_MINOR_AV_CAMCORDER = 0x0D, // Camcorder + COD_MINOR_AV_VIDEO_MONITOR = 0x0E, // Video Monitor + COD_MINOR_AV_VIDEO_DISPLAY_LOUD = 0x0F, // Video Display and Loudspeaker + COD_MINOR_AV_VIDEO_CONFERENCING = 0x10, // Video Conferencing device + COD_MINOR_AV_RESERVED = 0x11, // RESERVED + COD_MINOR_AV_GAMING_TOY = 0x12, // Gaming/Toy device +} av_minor_class_t; + +// Enum for the lower bits of the Peripheral minor device class (bits 5-2) +// This portion specifies the type of pointing device or remote control. +typedef enum : uint8_t { + COD_MINOR_PERIPHERA_UNCATEGORIZED = 0x00, + COD_MINOR_PERIPHERAL_MOUSE_UNCATEGORIZED = 0x00 | 0x0, // Uncategorized + COD_MINOR_PERIPHERAL_MOUSE_JOYSTICK = 0x01 | 0x20, // Joystick + COD_MINOR_PERIPHERAL_MOUSE_GAMEPAD = 0x02 | 0x20, // Gamepad + COD_MINOR_PERIPHERAL_MOUSE_REMOTE_CONTROL = 0x03 | 0x20, // Remote Control + COD_MINOR_PERIPHERAL_MOUSE_SENSING_DEVICE = 0x04 | 0x20, // Sensing Device + COD_MINOR_PERIPHERAL_MOUSE_DIGITIZER_TABLET = 0x05 | 0x20, // Digitizer Tablet + COD_MINOR_PERIPHERAL_MOUSE_CARD_READER = 0x06 | 0x20, // Card Reader + COD_MINOR_PERIPHERAL_MOUSE_DIGITAL_PEN = 0x07 | 0x20, // Digital pen + COD_MINOR_PERIPHERAL_MOUSE_HANDHELD_SCANNER = 0x08 | 0x20, // HandHeld Scnner + COD_MINOR_PERIPHERAL_MOUSE_HANDHELD_G_INPUT = 0x09 | 0x20, // HandheldGestural InputDevice(e.g., ”wand” form factor) + + COD_MINOR_PERIPHERAL_KBD_UNCATEGORIZED = 0x00 | 0x10, // Uncategorized + COD_MINOR_PERIPHERAL_KBD_JOYSTICK = 0x01 | 0x10, // Joystick + COD_MINOR_PERIPHERAL_KBD_GAMEPAD = 0x02 | 0x10, // Gamepad + COD_MINOR_PERIPHERAL_KBD_REMOTE_CONTROL = 0x03 | 0x10, // Remote Control + COD_MINOR_PERIPHERAL_KBD_SENSING_DEVICE = 0x04 | 0x10, // Sensing Device + COD_MINOR_PERIPHERAL_KBD_DIGITIZER_TABLET = 0x05 | 0x10, // Digitizer Tablet + COD_MINOR_PERIPHERAL_KBD_CARD_READER = 0x06 | 0x10, // Card Reader + COD_MINOR_PERIPHERAL_KBD_DIGITAL_PEN = 0x07 | 0x10, // Digital pen + COD_MINOR_PERIPHERAL_KBD_HANDHELD_SCANNER = 0x08 | 0x10, // HandHeld Scnner + COD_MINOR_PERIPHERAL_KBD_HANDHELD_G_INPUT = 0x09 | 0x10, // HandheldGestural InputDevice(e.g., ”wand” form factor) + COD_MINOR_PERIPHERAL_KBD_MSE_UNCATEGORIZED = 0x00 | 0x30, // Uncategorized + COD_MINOR_PERIPHERAL_KBD_MSE_JOYSTICK = 0x01 | 0x30, // Joystick + COD_MINOR_PERIPHERAL_KBD_MSE_GAMEPAD = 0x02 | 0x30, // Gamepad + COD_MINOR_PERIPHERAL_KBD_MSE_REMOTE_CONTROL = 0x03 | 0x30, // Remote Control + COD_MINOR_PERIPHERAL_KBD_MSE_SENSING_DEVICE = 0x04 | 0x30, // Sensing Device + COD_MINOR_PERIPHERAL_KBD_MSE_DIGITIZER_TABLET = 0x05 | 0x30, // Digitizer Tablet + COD_MINOR_PERIPHERAL_KBD_MSE_CARD_READER = 0x06 | 0x30, // Card Reader + COD_MINOR_PERIPHERAL_KBD_MSE_DIGITAL_PEN = 0x07 | 0x30, // Digital pen + COD_MINOR_PERIPHERAL_KBD_MSE_HANDHELD_SCANNER = 0x08 | 0x30, // HandHeld Scnner + COD_MINOR_PERIPHERAL_KBD_MSE_HANDHELD_G_INPUT = 0x09 | 0x30 // HandheldGestural InputDevice(e.g., ”wand” form factor) +} peripheral_pointing_device_t; + +// Bitmask for the Imaging minor device class (bits 7-4) +// These bits can be combined using bitwise OR. +typedef enum : uint8_t { + COD_MINOR_IMAGING_DISPLAY_MASK = 0x04, // Bit 4: Display capable + COD_MINOR_IMAGING_CAMERA_MASK = 0x08, // Bit 5: Camera capable + COD_MINOR_IMAGING_SCANNER_MASK = 0x10, // Bit 6: Scanner capable + COD_MINOR_IMAGING_PRINTER_MASK = 0x20 // Bit 7: Printer capable +} imaging_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'Wearable' +typedef enum : uint8_t { + COD_MINOR_WEARABLE_UNCATEGORIZED = 0x00, // Uncategorized + COD_MINOR_WEARABLE_WRIST_WATCH = 0x01, // A wristwatch + COD_MINOR_WEARABLE_PAGER = 0x02, // A pager + COD_MINOR_WEARABLE_JACKET = 0x03, // A jacket + COD_MINOR_WEARABLE_HELMET = 0x04, // A helmet + COD_MINOR_WEARABLE_GLASSES = 0x05, // Glasses, such as those with a display + COD_MINOR_WEARABLE_PIN = 0x06 // PIN +} wearable_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'Toy' +typedef enum : uint8_t { + COD_MINOR_TOY_UNCATEGORIZED = 0x00, // Uncategorized toy + COD_MINOR_TOY_ROBOT = 0x01, // Robot toy + COD_MINOR_TOY_VEHICLE = 0x02, // Vehicle toy + COD_MINOR_TOY_DOLL = 0x03, // Doll toy + COD_MINOR_TOY_CONTROLLER = 0x04, // Controller toy + COD_MINOR_TOY_GAME = 0x05 // Game toy +} toy_minor_class_t; + +// Enum for Minor Device Class when Major Class is 'Health' +typedef enum : uint8_t { + COD_MINOR_HEALTH_UNDEFINED = 0x00, // Undefined Health Device + COD_MINOR_HEALTH_BLOOD_PRESSURE = 0x01, // Blood Pressure Monitor + COD_MINOR_HEALTH_THERMOMETER = 0x02, // Thermometer + COD_MINOR_HEALTH_WEIGHING_SCALE = 0x03, // Weighing Scale + COD_MINOR_HEALTH_GLUCOSE_METER = 0x04, // Glucose Meter + COD_MINOR_HEALTH_PULSE_OXIMETER = 0x05, // Pulse Oximeter + COD_MINOR_HEALTH_HEART_RATE_MONITOR = 0x06, // Heart/Pulse Rate Monitor + COD_MINOR_HEALTH_HEALTH_DATA_DISPLAY = 0x07, // Health Data Display + COD_MINOR_HEALTH_STEP_COUNTER = 0x08, // Step Counter + COD_MINOR_HEALTH_BODY_COMPOSITION = 0x09, // Body Composition Analyzer + COD_MINOR_HEALTH_PEAK_FLOW_MONITOR = 0x0A, // Peak Flow Monitor + COD_MINOR_HEALTH_MEDICATION_MONITOR = 0x0B, // Medication Monitor + COD_MINOR_HEALTH_KNEE_PROTH = 0x0C, // KneeProsthesis + COD_MINOR_HEALTH_ANKLE_PROTH = 0x0D, // AnkleProsthesis + COD_MINOR_HEALTH_GENERIC_HEATH_MGR = 0x0E, // GenericHealthManager + COD_MINOR_HEALTH_PERSONAL_MOBLTY = 0x0F // PersonalMobilityDevice +} health_minor_class_t; + +static service_class_t getH_LService(bluetooth_cod_t cod); +// Function to construct a CoD from its components +bluetooth_cod_t encodeClassOfDevice(service_class_t services, major_device_class_t major, uint8_t minor); + +std::string decodeClassOfDevice(bluetooth_cod_t cod); +std::string decodeClassOfDevice(service_class_t services, major_device_class_t major, uint8_t minor); +std::string decodeMajorMinor(major_device_class_t major, uint8_t minor); + +std::vector makeATT_Payload_CodeClassOfDevice(bluetooth_cod_t cod); + +} // namespace NimClassOfDeviceType + +#if defined(CONFIG_NIMBLE_USE_MAGIC_ENUM) +namespace magic_enum::customize { + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::service_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::major_device_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::phone_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::lan_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::av_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::peripheral_pointing_device_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::imaging_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::wearable_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::toy_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::health_minor_class_t) -> std::true_type; + +// enable flag use by the magic enum for debug print +constexpr auto magic_enum_is_flags(NimClassOfDeviceType::computer_minor_class_t) -> std::true_type; + +} // namespace magic_enum::customize +#endif \ No newline at end of file