/* * BLEUtils.cpp * * Created on: Mar 25, 2017 * Author: kolban */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include "BLEAddress.h" #include "BLEClient.h" #include "BLEUtils.h" #include "BLEUUID.h" #include "GeneralUtils.h" #include #include #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 ESP-IDF #include // Part of C++ STL #include #include #include "esp32-hal-log.h" /* static std::map g_addressMap; static std::map g_connIdMap; */ typedef struct { uint32_t assignedNumber; const char* name; } member_t; static const member_t members_ids[] = { #if CONFIG_LOG_DEFAULT_LEVEL > 4 {0xFE08, "Microsoft"}, {0xFE09, "Pillsy, Inc."}, {0xFE0A, "ruwido austria gmbh"}, {0xFE0B, "ruwido austria gmbh"}, {0xFE0C, "Procter & Gamble"}, {0xFE0D, "Procter & Gamble"}, {0xFE0E, "Setec Pty Ltd"}, {0xFE0F, "Philips Lighting B.V."}, {0xFE10, "Lapis Semiconductor Co., Ltd."}, {0xFE11, "GMC-I Messtechnik GmbH"}, {0xFE12, "M-Way Solutions GmbH"}, {0xFE13, "Apple Inc."}, {0xFE14, "Flextronics International USA Inc."}, {0xFE15, "Amazon Fulfillment Services, Inc."}, {0xFE16, "Footmarks, Inc."}, {0xFE17, "Telit Wireless Solutions GmbH"}, {0xFE18, "Runtime, Inc."}, {0xFE19, "Google Inc."}, {0xFE1A, "Tyto Life LLC"}, {0xFE1B, "Tyto Life LLC"}, {0xFE1C, "NetMedia, Inc."}, {0xFE1D, "Illuminati Instrument Corporation"}, {0xFE1E, "Smart Innovations Co., Ltd"}, {0xFE1F, "Garmin International, Inc."}, {0xFE20, "Emerson"}, {0xFE21, "Bose Corporation"}, {0xFE22, "Zoll Medical Corporation"}, {0xFE23, "Zoll Medical Corporation"}, {0xFE24, "August Home Inc"}, {0xFE25, "Apple, Inc. "}, {0xFE26, "Google Inc."}, {0xFE27, "Google Inc."}, {0xFE28, "Ayla Networks"}, {0xFE29, "Gibson Innovations"}, {0xFE2A, "DaisyWorks, Inc."}, {0xFE2B, "ITT Industries"}, {0xFE2C, "Google Inc."}, {0xFE2D, "SMART INNOVATION Co.,Ltd"}, {0xFE2E, "ERi,Inc."}, {0xFE2F, "CRESCO Wireless, Inc"}, {0xFE30, "Volkswagen AG"}, {0xFE31, "Volkswagen AG"}, {0xFE32, "Pro-Mark, Inc."}, {0xFE33, "CHIPOLO d.o.o."}, {0xFE34, "SmallLoop LLC"}, {0xFE35, "HUAWEI Technologies Co., Ltd"}, {0xFE36, "HUAWEI Technologies Co., Ltd"}, {0xFE37, "Spaceek LTD"}, {0xFE38, "Spaceek LTD"}, {0xFE39, "TTS Tooltechnic Systems AG & Co. KG"}, {0xFE3A, "TTS Tooltechnic Systems AG & Co. KG"}, {0xFE3B, "Dolby Laboratories"}, {0xFE3C, "Alibaba"}, {0xFE3D, "BD Medical"}, {0xFE3E, "BD Medical"}, {0xFE3F, "Friday Labs Limited"}, {0xFE40, "Inugo Systems Limited"}, {0xFE41, "Inugo Systems Limited"}, {0xFE42, "Nets A/S "}, {0xFE43, "Andreas Stihl AG & Co. KG"}, {0xFE44, "SK Telecom "}, {0xFE45, "Snapchat Inc"}, {0xFE46, "B&O Play A/S "}, {0xFE47, "General Motors"}, {0xFE48, "General Motors"}, {0xFE49, "SenionLab AB"}, {0xFE4A, "OMRON HEALTHCARE Co., Ltd."}, {0xFE4B, "Philips Lighting B.V."}, {0xFE4C, "Volkswagen AG"}, {0xFE4D, "Casambi Technologies Oy"}, {0xFE4E, "NTT docomo"}, {0xFE4F, "Molekule, Inc."}, {0xFE50, "Google Inc."}, {0xFE51, "SRAM"}, {0xFE52, "SetPoint Medical"}, {0xFE53, "3M"}, {0xFE54, "Motiv, Inc."}, {0xFE55, "Google Inc."}, {0xFE56, "Google Inc."}, {0xFE57, "Dotted Labs"}, {0xFE58, "Nordic Semiconductor ASA"}, {0xFE59, "Nordic Semiconductor ASA"}, {0xFE5A, "Chronologics Corporation"}, {0xFE5B, "GT-tronics HK Ltd"}, {0xFE5C, "million hunters GmbH"}, {0xFE5D, "Grundfos A/S"}, {0xFE5E, "Plastc Corporation"}, {0xFE5F, "Eyefi, Inc."}, {0xFE60, "Lierda Science & Technology Group Co., Ltd."}, {0xFE61, "Logitech International SA"}, {0xFE62, "Indagem Tech LLC"}, {0xFE63, "Connected Yard, Inc."}, {0xFE64, "Siemens AG"}, {0xFE65, "CHIPOLO d.o.o."}, {0xFE66, "Intel Corporation"}, {0xFE67, "Lab Sensor Solutions"}, {0xFE68, "Qualcomm Life Inc"}, {0xFE69, "Qualcomm Life Inc"}, {0xFE6A, "Kontakt Micro-Location Sp. z o.o."}, {0xFE6B, "TASER International, Inc."}, {0xFE6C, "TASER International, Inc."}, {0xFE6D, "The University of Tokyo"}, {0xFE6E, "The University of Tokyo"}, {0xFE6F, "LINE Corporation"}, {0xFE70, "Beijing Jingdong Century Trading Co., Ltd."}, {0xFE71, "Plume Design Inc"}, {0xFE72, "St. Jude Medical, Inc."}, {0xFE73, "St. Jude Medical, Inc."}, {0xFE74, "unwire"}, {0xFE75, "TangoMe"}, {0xFE76, "TangoMe"}, {0xFE77, "Hewlett-Packard Company"}, {0xFE78, "Hewlett-Packard Company"}, {0xFE79, "Zebra Technologies"}, {0xFE7A, "Bragi GmbH"}, {0xFE7B, "Orion Labs, Inc."}, {0xFE7C, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, {0xFE7D, "Aterica Health Inc."}, {0xFE7E, "Awear Solutions Ltd"}, {0xFE7F, "Doppler Lab"}, {0xFE80, "Doppler Lab"}, {0xFE81, "Medtronic Inc."}, {0xFE82, "Medtronic Inc."}, {0xFE83, "Blue Bite"}, {0xFE84, "RF Digital Corp"}, {0xFE85, "RF Digital Corp"}, {0xFE86, "HUAWEI Technologies Co., Ltd. ( )"}, {0xFE87, "Qingdao Yeelink Information Technology Co., Ltd. ( )"}, {0xFE88, "SALTO SYSTEMS S.L."}, {0xFE89, "B&O Play A/S"}, {0xFE8A, "Apple, Inc."}, {0xFE8B, "Apple, Inc."}, {0xFE8C, "TRON Forum"}, {0xFE8D, "Interaxon Inc."}, {0xFE8E, "ARM Ltd"}, {0xFE8F, "CSR"}, {0xFE90, "JUMA"}, {0xFE91, "Shanghai Imilab Technology Co.,Ltd"}, {0xFE92, "Jarden Safety & Security"}, {0xFE93, "OttoQ Inc."}, {0xFE94, "OttoQ Inc."}, {0xFE95, "Xiaomi Inc."}, {0xFE96, "Tesla Motor Inc."}, {0xFE97, "Tesla Motor Inc."}, {0xFE98, "Currant, Inc."}, {0xFE99, "Currant, Inc."}, {0xFE9A, "Estimote"}, {0xFE9B, "Samsara Networks, Inc"}, {0xFE9C, "GSI Laboratories, Inc."}, {0xFE9D, "Mobiquity Networks Inc"}, {0xFE9E, "Dialog Semiconductor B.V."}, {0xFE9F, "Google Inc."}, {0xFEA0, "Google Inc."}, {0xFEA1, "Intrepid Control Systems, Inc."}, {0xFEA2, "Intrepid Control Systems, Inc."}, {0xFEA3, "ITT Industries"}, {0xFEA4, "Paxton Access Ltd"}, {0xFEA5, "GoPro, Inc."}, {0xFEA6, "GoPro, Inc."}, {0xFEA7, "UTC Fire and Security"}, {0xFEA8, "Savant Systems LLC"}, {0xFEA9, "Savant Systems LLC"}, {0xFEAA, "Google Inc."}, {0xFEAB, "Nokia Corporation"}, {0xFEAC, "Nokia Corporation"}, {0xFEAD, "Nokia Corporation"}, {0xFEAE, "Nokia Corporation"}, {0xFEAF, "Nest Labs Inc."}, {0xFEB0, "Nest Labs Inc."}, {0xFEB1, "Electronics Tomorrow Limited"}, {0xFEB2, "Microsoft Corporation"}, {0xFEB3, "Taobao"}, {0xFEB4, "WiSilica Inc."}, {0xFEB5, "WiSilica Inc."}, {0xFEB6, "Vencer Co, Ltd"}, {0xFEB7, "Facebook, Inc."}, {0xFEB8, "Facebook, Inc."}, {0xFEB9, "LG Electronics"}, {0xFEBA, "Tencent Holdings Limited"}, {0xFEBB, "adafruit industries"}, {0xFEBC, "Dexcom, Inc. "}, {0xFEBD, "Clover Network, Inc."}, {0xFEBE, "Bose Corporation"}, {0xFEBF, "Nod, Inc."}, {0xFEC0, "KDDI Corporation"}, {0xFEC1, "KDDI Corporation"}, {0xFEC2, "Blue Spark Technologies, Inc."}, {0xFEC3, "360fly, Inc."}, {0xFEC4, "PLUS Location Systems"}, {0xFEC5, "Realtek Semiconductor Corp."}, {0xFEC6, "Kocomojo, LLC"}, {0xFEC7, "Apple, Inc."}, {0xFEC8, "Apple, Inc."}, {0xFEC9, "Apple, Inc."}, {0xFECA, "Apple, Inc."}, {0xFECB, "Apple, Inc."}, {0xFECC, "Apple, Inc."}, {0xFECD, "Apple, Inc."}, {0xFECE, "Apple, Inc."}, {0xFECF, "Apple, Inc."}, {0xFED0, "Apple, Inc."}, {0xFED1, "Apple, Inc."}, {0xFED2, "Apple, Inc."}, {0xFED3, "Apple, Inc."}, {0xFED4, "Apple, Inc."}, {0xFED5, "Plantronics Inc."}, {0xFED6, "Broadcom Corporation"}, {0xFED7, "Broadcom Corporation"}, {0xFED8, "Google Inc."}, {0xFED9, "Pebble Technology Corporation"}, {0xFEDA, "ISSC Technologies Corporation"}, {0xFEDB, "Perka, Inc."}, {0xFEDC, "Jawbone"}, {0xFEDD, "Jawbone"}, {0xFEDE, "Coin, Inc."}, {0xFEDF, "Design SHIFT"}, {0xFEE0, "Anhui Huami Information Technology Co."}, {0xFEE1, "Anhui Huami Information Technology Co."}, {0xFEE2, "Anki, Inc."}, {0xFEE3, "Anki, Inc."}, {0xFEE4, "Nordic Semiconductor ASA"}, {0xFEE5, "Nordic Semiconductor ASA"}, {0xFEE6, "Silvair, Inc."}, {0xFEE7, "Tencent Holdings Limited"}, {0xFEE8, "Quintic Corp."}, {0xFEE9, "Quintic Corp."}, {0xFEEA, "Swirl Networks, Inc."}, {0xFEEB, "Swirl Networks, Inc."}, {0xFEEC, "Tile, Inc."}, {0xFEED, "Tile, Inc."}, {0xFEEE, "Polar Electro Oy"}, {0xFEEF, "Polar Electro Oy"}, {0xFEF0, "Intel"}, {0xFEF1, "CSR"}, {0xFEF2, "CSR"}, {0xFEF3, "Google Inc."}, {0xFEF4, "Google Inc."}, {0xFEF5, "Dialog Semiconductor GmbH"}, {0xFEF6, "Wicentric, Inc."}, {0xFEF7, "Aplix Corporation"}, {0xFEF8, "Aplix Corporation"}, {0xFEF9, "PayPal, Inc."}, {0xFEFA, "PayPal, Inc."}, {0xFEFB, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, {0xFEFC, "Gimbal, Inc."}, {0xFEFD, "Gimbal, Inc."}, {0xFEFE, "GN ReSound A/S"}, {0xFEFF, "GN Netcom"}, {0xFFFF, "Reserved"}, /*for testing purposes only*/ #endif {0, "" } }; typedef struct { uint32_t assignedNumber; const char* name; } gattdescriptor_t; static const gattdescriptor_t g_descriptor_ids[] = { #if CONFIG_LOG_DEFAULT_LEVEL > 4 {0x2905,"Characteristic Aggregate Format"}, {0x2900,"Characteristic Extended Properties"}, {0x2904,"Characteristic Presentation Format"}, {0x2901,"Characteristic User Description"}, {0x2902,"Client Characteristic Configuration"}, {0x290B,"Environmental Sensing Configuration"}, {0x290C,"Environmental Sensing Measurement"}, {0x290D,"Environmental Sensing Trigger Setting"}, {0x2907,"External Report Reference"}, {0x2909,"Number of Digitals"}, {0x2908,"Report Reference"}, {0x2903,"Server Characteristic Configuration"}, {0x290E,"Time Trigger Setting"}, {0x2906,"Valid Range"}, {0x290A,"Value Trigger Setting"}, #endif { 0, "" } }; typedef struct { uint32_t assignedNumber; const char* name; } characteristicMap_t; static const characteristicMap_t g_characteristicsMappings[] = { #if CONFIG_LOG_DEFAULT_LEVEL > 4 {0x2A7E,"Aerobic Heart Rate Lower Limit"}, {0x2A84,"Aerobic Heart Rate Upper Limit"}, {0x2A7F,"Aerobic Threshold"}, {0x2A80,"Age"}, {0x2A5A,"Aggregate"}, {0x2A43,"Alert Category ID"}, {0x2A42,"Alert Category ID Bit Mask"}, {0x2A06,"Alert Level"}, {0x2A44,"Alert Notification Control Point"}, {0x2A3F,"Alert Status"}, {0x2AB3,"Altitude"}, {0x2A81,"Anaerobic Heart Rate Lower Limit"}, {0x2A82,"Anaerobic Heart Rate Upper Limit"}, {0x2A83,"Anaerobic Threshold"}, {0x2A58,"Analog"}, {0x2A59,"Analog Output"}, {0x2A73,"Apparent Wind Direction"}, {0x2A72,"Apparent Wind Speed"}, {0x2A01,"Appearance"}, {0x2AA3,"Barometric Pressure Trend"}, {0x2A19,"Battery Level"}, {0x2A1B,"Battery Level State"}, {0x2A1A,"Battery Power State"}, {0x2A49,"Blood Pressure Feature"}, {0x2A35,"Blood Pressure Measurement"}, {0x2A9B,"Body Composition Feature"}, {0x2A9C,"Body Composition Measurement"}, {0x2A38,"Body Sensor Location"}, {0x2AA4,"Bond Management Control Point"}, {0x2AA5,"Bond Management Features"}, {0x2A22,"Boot Keyboard Input Report"}, {0x2A32,"Boot Keyboard Output Report"}, {0x2A33,"Boot Mouse Input Report"}, {0x2AA6,"Central Address Resolution"}, {0x2AA8,"CGM Feature"}, {0x2AA7,"CGM Measurement"}, {0x2AAB,"CGM Session Run Time"}, {0x2AAA,"CGM Session Start Time"}, {0x2AAC,"CGM Specific Ops Control Point"}, {0x2AA9,"CGM Status"}, {0x2ACE,"Cross Trainer Data"}, {0x2A5C,"CSC Feature"}, {0x2A5B,"CSC Measurement"}, {0x2A2B,"Current Time"}, {0x2A66,"Cycling Power Control Point"}, {0x2A66,"Cycling Power Control Point"}, {0x2A65,"Cycling Power Feature"}, {0x2A65,"Cycling Power Feature"}, {0x2A63,"Cycling Power Measurement"}, {0x2A64,"Cycling Power Vector"}, {0x2A99,"Database Change Increment"}, {0x2A85,"Date of Birth"}, {0x2A86,"Date of Threshold Assessment"}, {0x2A08,"Date Time"}, {0x2A0A,"Day Date Time"}, {0x2A09,"Day of Week"}, {0x2A7D,"Descriptor Value Changed"}, {0x2A00,"Device Name"}, {0x2A7B,"Dew Point"}, {0x2A56,"Digital"}, {0x2A57,"Digital Output"}, {0x2A0D,"DST Offset"}, {0x2A6C,"Elevation"}, {0x2A87,"Email Address"}, {0x2A0B,"Exact Time 100"}, {0x2A0C,"Exact Time 256"}, {0x2A88,"Fat Burn Heart Rate Lower Limit"}, {0x2A89,"Fat Burn Heart Rate Upper Limit"}, {0x2A26,"Firmware Revision String"}, {0x2A8A,"First Name"}, {0x2AD9,"Fitness Machine Control Point"}, {0x2ACC,"Fitness Machine Feature"}, {0x2ADA,"Fitness Machine Status"}, {0x2A8B,"Five Zone Heart Rate Limits"}, {0x2AB2,"Floor Number"}, {0x2A8C,"Gender"}, {0x2A51,"Glucose Feature"}, {0x2A18,"Glucose Measurement"}, {0x2A34,"Glucose Measurement Context"}, {0x2A74,"Gust Factor"}, {0x2A27,"Hardware Revision String"}, {0x2A39,"Heart Rate Control Point"}, {0x2A8D,"Heart Rate Max"}, {0x2A37,"Heart Rate Measurement"}, {0x2A7A,"Heat Index"}, {0x2A8E,"Height"}, {0x2A4C,"HID Control Point"}, {0x2A4A,"HID Information"}, {0x2A8F,"Hip Circumference"}, {0x2ABA,"HTTP Control Point"}, {0x2AB9,"HTTP Entity Body"}, {0x2AB7,"HTTP Headers"}, {0x2AB8,"HTTP Status Code"}, {0x2ABB,"HTTPS Security"}, {0x2A6F,"Humidity"}, {0x2A2A,"IEEE 11073-20601 Regulatory Certification Data List"}, {0x2AD2,"Indoor Bike Data"}, {0x2AAD,"Indoor Positioning Configuration"}, {0x2A36,"Intermediate Cuff Pressure"}, {0x2A1E,"Intermediate Temperature"}, {0x2A77,"Irradiance"}, {0x2AA2,"Language"}, {0x2A90,"Last Name"}, {0x2AAE,"Latitude"}, {0x2A6B,"LN Control Point"}, {0x2A6A,"LN Feature"}, {0x2AB1,"Local East Coordinate"}, {0x2AB0,"Local North Coordinate"}, {0x2A0F,"Local Time Information"}, {0x2A67,"Location and Speed Characteristic"}, {0x2AB5,"Location Name"}, {0x2AAF,"Longitude"}, {0x2A2C,"Magnetic Declination"}, {0x2AA0,"Magnetic Flux Density - 2D"}, {0x2AA1,"Magnetic Flux Density - 3D"}, {0x2A29,"Manufacturer Name String"}, {0x2A91,"Maximum Recommended Heart Rate"}, {0x2A21,"Measurement Interval"}, {0x2A24,"Model Number String"}, {0x2A68,"Navigation"}, {0x2A3E,"Network Availability"}, {0x2A46,"New Alert"}, {0x2AC5,"Object Action Control Point"}, {0x2AC8,"Object Changed"}, {0x2AC1,"Object First-Created"}, {0x2AC3,"Object ID"}, {0x2AC2,"Object Last-Modified"}, {0x2AC6,"Object List Control Point"}, {0x2AC7,"Object List Filter"}, {0x2ABE,"Object Name"}, {0x2AC4,"Object Properties"}, {0x2AC0,"Object Size"}, {0x2ABF,"Object Type"}, {0x2ABD,"OTS Feature"}, {0x2A04,"Peripheral Preferred Connection Parameters"}, {0x2A02,"Peripheral Privacy Flag"}, {0x2A5F,"PLX Continuous Measurement Characteristic"}, {0x2A60,"PLX Features"}, {0x2A5E,"PLX Spot-Check Measurement"}, {0x2A50,"PnP ID"}, {0x2A75,"Pollen Concentration"}, {0x2A2F,"Position 2D"}, {0x2A30,"Position 3D"}, {0x2A69,"Position Quality"}, {0x2A6D,"Pressure"}, {0x2A4E,"Protocol Mode"}, {0x2A62,"Pulse Oximetry Control Point"}, {0x2A60,"Pulse Oximetry Pulsatile Event Characteristic"}, {0x2A78,"Rainfall"}, {0x2A03,"Reconnection Address"}, {0x2A52,"Record Access Control Point"}, {0x2A14,"Reference Time Information"}, {0x2A3A,"Removable"}, {0x2A4D,"Report"}, {0x2A4B,"Report Map"}, {0x2AC9,"Resolvable Private Address Only"}, {0x2A92,"Resting Heart Rate"}, {0x2A40,"Ringer Control point"}, {0x2A41,"Ringer Setting"}, {0x2AD1,"Rower Data"}, {0x2A54,"RSC Feature"}, {0x2A53,"RSC Measurement"}, {0x2A55,"SC Control Point"}, {0x2A4F,"Scan Interval Window"}, {0x2A31,"Scan Refresh"}, {0x2A3C,"Scientific Temperature Celsius"}, {0x2A10,"Secondary Time Zone"}, {0x2A5D,"Sensor Location"}, {0x2A25,"Serial Number String"}, {0x2A05,"Service Changed"}, {0x2A3B,"Service Required"}, {0x2A28,"Software Revision String"}, {0x2A93,"Sport Type for Aerobic and Anaerobic Thresholds"}, {0x2AD0,"Stair Climber Data"}, {0x2ACF,"Step Climber Data"}, {0x2A3D,"String"}, {0x2AD7,"Supported Heart Rate Range"}, {0x2AD5,"Supported Inclination Range"}, {0x2A47,"Supported New Alert Category"}, {0x2AD8,"Supported Power Range"}, {0x2AD6,"Supported Resistance Level Range"}, {0x2AD4,"Supported Speed Range"}, {0x2A48,"Supported Unread Alert Category"}, {0x2A23,"System ID"}, {0x2ABC,"TDS Control Point"}, {0x2A6E,"Temperature"}, {0x2A1F,"Temperature Celsius"}, {0x2A20,"Temperature Fahrenheit"}, {0x2A1C,"Temperature Measurement"}, {0x2A1D,"Temperature Type"}, {0x2A94,"Three Zone Heart Rate Limits"}, {0x2A12,"Time Accuracy"}, {0x2A15,"Time Broadcast"}, {0x2A13,"Time Source"}, {0x2A16,"Time Update Control Point"}, {0x2A17,"Time Update State"}, {0x2A11,"Time with DST"}, {0x2A0E,"Time Zone"}, {0x2AD3,"Training Status"}, {0x2ACD,"Treadmill Data"}, {0x2A71,"True Wind Direction"}, {0x2A70,"True Wind Speed"}, {0x2A95,"Two Zone Heart Rate Limit"}, {0x2A07,"Tx Power Level"}, {0x2AB4,"Uncertainty"}, {0x2A45,"Unread Alert Status"}, {0x2AB6,"URI"}, {0x2A9F,"User Control Point"}, {0x2A9A,"User Index"}, {0x2A76,"UV Index"}, {0x2A96,"VO2 Max"}, {0x2A97,"Waist Circumference"}, {0x2A98,"Weight"}, {0x2A9D,"Weight Measurement"}, {0x2A9E,"Weight Scale Feature"}, {0x2A79,"Wind Chill"}, #endif {0, ""} }; /** * @brief Mapping from service ids to names */ typedef struct { const char* name; const char* type; uint32_t assignedNumber; } gattService_t; /** * Definition of the service ids to names that we know about. */ static const gattService_t g_gattServices[] = { #if CONFIG_LOG_DEFAULT_LEVEL > 4 {"Alert Notification Service", "org.bluetooth.service.alert_notification", 0x1811}, {"Automation IO", "org.bluetooth.service.automation_io", 0x1815 }, {"Battery Service","org.bluetooth.service.battery_service", 0x180F}, {"Blood Pressure", "org.bluetooth.service.blood_pressure", 0x1810}, {"Body Composition", "org.bluetooth.service.body_composition", 0x181B}, {"Bond Management", "org.bluetooth.service.bond_management", 0x181E}, {"Continuous Glucose Monitoring", "org.bluetooth.service.continuous_glucose_monitoring", 0x181F}, {"Current Time Service", "org.bluetooth.service.current_time", 0x1805}, {"Cycling Power", "org.bluetooth.service.cycling_power", 0x1818}, {"Cycling Speed and Cadence", "org.bluetooth.service.cycling_speed_and_cadence", 0x1816}, {"Device Information", "org.bluetooth.service.device_information", 0x180A}, {"Environmental Sensing", "org.bluetooth.service.environmental_sensing", 0x181A}, {"Generic Access", "org.bluetooth.service.generic_access", 0x1800}, {"Generic Attribute", "org.bluetooth.service.generic_attribute", 0x1801}, {"Glucose", "org.bluetooth.service.glucose", 0x1808}, {"Health Thermometer", "org.bluetooth.service.health_thermometer", 0x1809}, {"Heart Rate", "org.bluetooth.service.heart_rate", 0x180D}, {"HTTP Proxy", "org.bluetooth.service.http_proxy", 0x1823}, {"Human Interface Device", "org.bluetooth.service.human_interface_device", 0x1812}, {"Immediate Alert", "org.bluetooth.service.immediate_alert", 0x1802}, {"Indoor Positioning", "org.bluetooth.service.indoor_positioning", 0x1821}, {"Internet Protocol Support", "org.bluetooth.service.internet_protocol_support", 0x1820}, {"Link Loss", "org.bluetooth.service.link_loss", 0x1803}, {"Location and Navigation", "org.bluetooth.service.location_and_navigation", 0x1819}, {"Next DST Change Service", "org.bluetooth.service.next_dst_change", 0x1807}, {"Object Transfer", "org.bluetooth.service.object_transfer", 0x1825}, {"Phone Alert Status Service", "org.bluetooth.service.phone_alert_status", 0x180E}, {"Pulse Oximeter", "org.bluetooth.service.pulse_oximeter", 0x1822}, {"Reference Time Update Service", "org.bluetooth.service.reference_time_update", 0x1806}, {"Running Speed and Cadence", "org.bluetooth.service.running_speed_and_cadence", 0x1814}, {"Scan Parameters", "org.bluetooth.service.scan_parameters", 0x1813}, {"Transport Discovery", "org.bluetooth.service.transport_discovery", 0x1824}, {"Tx Power", "org.bluetooth.service.tx_power", 0x1804}, {"User Data", "org.bluetooth.service.user_data", 0x181C}, {"Weight Scale", "org.bluetooth.service.weight_scale", 0x181D}, #endif {"", "", 0 } }; /** * @brief Convert characteristic properties into a string representation. * @param [in] prop Characteristic properties. * @return A string representation of characteristic properties. */ std::string BLEUtils::characteristicPropertiesToString(esp_gatt_char_prop_t prop) { std::stringstream stream; stream << "broadcast: " << ((prop & ESP_GATT_CHAR_PROP_BIT_BROADCAST)?"1":"0") << ", read: " << ((prop & ESP_GATT_CHAR_PROP_BIT_READ)?"1":"0") << ", write_nr: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)?"1":"0") << ", write: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE)?"1":"0") << ", notify: " << ((prop & ESP_GATT_CHAR_PROP_BIT_NOTIFY)?"1":"0") << ", indicate: " << ((prop & ESP_GATT_CHAR_PROP_BIT_INDICATE)?"1":"0") << ", auth: " << ((prop & ESP_GATT_CHAR_PROP_BIT_AUTH)?"1":"0"); return stream.str(); } // characteristicPropertiesToString /** * @brief Convert an esp_gatt_id_t to a string. */ static std::string gattIdToString(esp_gatt_id_t gattId) { std::stringstream stream; stream << "uuid: " << BLEUUID(gattId.uuid).toString() << ", inst_id: " << (int)gattId.inst_id; //sprintf(buffer, "uuid: %s, inst_id: %d", uuidToString(gattId.uuid).c_str(), gattId.inst_id); return stream.str(); } // gattIdToString /** * @brief Convert an esp_ble_addr_type_t to a string representation. */ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { switch (type) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case BLE_ADDR_TYPE_PUBLIC: return "BLE_ADDR_TYPE_PUBLIC"; case BLE_ADDR_TYPE_RANDOM: return "BLE_ADDR_TYPE_RANDOM"; case BLE_ADDR_TYPE_RPA_PUBLIC: return "BLE_ADDR_TYPE_RPA_PUBLIC"; case BLE_ADDR_TYPE_RPA_RANDOM: return "BLE_ADDR_TYPE_RPA_RANDOM"; #endif default: return " esp_ble_addr_type_t"; } } // addressTypeToString /** * @brief Convert the BLE Advertising Data flags to a string. * @param adFlags The flags to convert * @return std::string A string representation of the advertising flags. */ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { std::stringstream ss; if (adFlags & (1 << 0)) { ss << "[LE Limited Discoverable Mode] "; } if (adFlags & (1 << 1)) { ss << "[LE General Discoverable Mode] "; } if (adFlags & (1 << 2)) { ss << "[BR/EDR Not Supported] "; } if (adFlags & (1 << 3)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Controller)] "; } if (adFlags & (1 << 4)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Host)] "; } return ss.str(); } // adFlagsToString /** * @brief Given an advertising type, return a string representation of the type. * * For details see ... * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile * * @return A string representation of the type. */ const char* BLEUtils::advTypeToString(uint8_t advType) { switch (advType) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_BLE_AD_TYPE_FLAG: // 0x01 return "ESP_BLE_AD_TYPE_FLAG"; case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 return "ESP_BLE_AD_TYPE_16SRV_PART"; case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 return "ESP_BLE_AD_TYPE_16SRV_CMPL"; case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 return "ESP_BLE_AD_TYPE_32SRV_PART"; case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 return "ESP_BLE_AD_TYPE_32SRV_CMPL"; case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 return "ESP_BLE_AD_TYPE_128SRV_PART"; case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 return "ESP_BLE_AD_TYPE_128SRV_CMPL"; case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 return "ESP_BLE_AD_TYPE_NAME_SHORT"; case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 return "ESP_BLE_AD_TYPE_NAME_CMPL"; case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a return "ESP_BLE_AD_TYPE_TX_PWR"; case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b return "ESP_BLE_AD_TYPE_DEV_CLASS"; case ESP_BLE_AD_TYPE_SM_TK: // 0x10 return "ESP_BLE_AD_TYPE_SM_TK"; case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 return "ESP_BLE_AD_TYPE_SM_OOB_FLAG"; case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 return "ESP_BLE_AD_TYPE_INT_RANGE"; case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14 return "ESP_BLE_AD_TYPE_SOL_SRV_UUID"; case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15 return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID"; case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 return "ESP_BLE_AD_TYPE_SERVICE_DATA"; case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 return "ESP_BLE_AD_TYPE_PUBLIC_TARGET"; case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 return "ESP_BLE_AD_TYPE_RANDOM_TARGET"; case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 return "ESP_BLE_AD_TYPE_APPEARANCE"; case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a return "ESP_BLE_AD_TYPE_ADV_INT"; case ESP_BLE_AD_TYPE_32SOL_SRV_UUID: return "ESP_BLE_AD_TYPE_32SOL_SRV_UUID"; case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 return "ESP_BLE_AD_TYPE_32SERVICE_DATA"; case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; #endif default: log_v(" adv data type: 0x%x", advType); return ""; } // End switch } // advTypeToString esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) { esp_gatt_id_t retGattId; retGattId.uuid = uuid; retGattId.inst_id = inst_id; return retGattId; } esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary) { esp_gatt_srvc_id_t retSrvcId; retSrvcId.id = gattId; retSrvcId.is_primary = is_primary; return retSrvcId; } /** * @brief Create a hex representation of data. * * @param [in] target Where to write the hex string. If this is null, we malloc storage. * @param [in] source The start of the binary data. * @param [in] length The length of the data to convert. * @return A pointer to the formatted buffer. */ char* BLEUtils::buildHexData(uint8_t* target, uint8_t* source, uint8_t length) { // Guard against too much data. if (length > 100) length = 100; if (target == nullptr) { target = (uint8_t*) malloc(length * 2 + 1); if (target == nullptr) { log_e("buildHexData: malloc failed"); return nullptr; } } char* startOfData = (char*) target; for (int i = 0; i < length; i++) { sprintf((char*) target, "%.2x", (char) *source); source++; target += 2; } // Handle the special case where there was no data. if (length == 0) { *startOfData = 0; } return startOfData; } // buildHexData /** * @brief Build a printable string of memory range. * Create a string representation of a piece of memory. Only printable characters will be included * while those that are not printable will be replaced with '.'. * @param [in] source Start of memory. * @param [in] length Length of memory. * @return A string representation of a piece of memory. */ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { std::ostringstream ss; for (int i = 0; i < length; i++) { char c = *source; ss << (isprint(c) ? c : '.'); source++; } return ss.str(); } // buildPrintData /** * @brief Convert a close/disconnect reason to a string. * @param [in] reason The close reason. * @return A string representation of the reason. */ std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { switch (reason) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATT_CONN_UNKNOWN: { return "ESP_GATT_CONN_UNKNOWN"; } case ESP_GATT_CONN_L2C_FAILURE: { return "ESP_GATT_CONN_L2C_FAILURE"; } case ESP_GATT_CONN_TIMEOUT: { return "ESP_GATT_CONN_TIMEOUT"; } case ESP_GATT_CONN_TERMINATE_PEER_USER: { return "ESP_GATT_CONN_TERMINATE_PEER_USER"; } case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: { return "ESP_GATT_CONN_TERMINATE_LOCAL_HOST"; } case ESP_GATT_CONN_FAIL_ESTABLISH: { return "ESP_GATT_CONN_FAIL_ESTABLISH"; } case ESP_GATT_CONN_LMP_TIMEOUT: { return "ESP_GATT_CONN_LMP_TIMEOUT"; } case ESP_GATT_CONN_CONN_CANCEL: { return "ESP_GATT_CONN_CONN_CANCEL"; } case ESP_GATT_CONN_NONE: { return "ESP_GATT_CONN_NONE"; } #endif default: { return "Unknown"; } } } // gattCloseReasonToString std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType) { switch (eventType) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATTC_ACL_EVT: return "ESP_GATTC_ACL_EVT"; case ESP_GATTC_ADV_DATA_EVT: return "ESP_GATTC_ADV_DATA_EVT"; case ESP_GATTC_ADV_VSC_EVT: return "ESP_GATTC_ADV_VSC_EVT"; case ESP_GATTC_BTH_SCAN_CFG_EVT: return "ESP_GATTC_BTH_SCAN_CFG_EVT"; case ESP_GATTC_BTH_SCAN_DIS_EVT: return "ESP_GATTC_BTH_SCAN_DIS_EVT"; case ESP_GATTC_BTH_SCAN_ENB_EVT: return "ESP_GATTC_BTH_SCAN_ENB_EVT"; case ESP_GATTC_BTH_SCAN_PARAM_EVT: return "ESP_GATTC_BTH_SCAN_PARAM_EVT"; case ESP_GATTC_BTH_SCAN_RD_EVT: return "ESP_GATTC_BTH_SCAN_RD_EVT"; case ESP_GATTC_BTH_SCAN_THR_EVT: return "ESP_GATTC_BTH_SCAN_THR_EVT"; case ESP_GATTC_CANCEL_OPEN_EVT: return "ESP_GATTC_CANCEL_OPEN_EVT"; case ESP_GATTC_CFG_MTU_EVT: return "ESP_GATTC_CFG_MTU_EVT"; case ESP_GATTC_CLOSE_EVT: return "ESP_GATTC_CLOSE_EVT"; case ESP_GATTC_CONGEST_EVT: return "ESP_GATTC_CONGEST_EVT"; case ESP_GATTC_CONNECT_EVT: return "ESP_GATTC_CONNECT_EVT"; case ESP_GATTC_DISCONNECT_EVT: return "ESP_GATTC_DISCONNECT_EVT"; case ESP_GATTC_ENC_CMPL_CB_EVT: return "ESP_GATTC_ENC_CMPL_CB_EVT"; case ESP_GATTC_EXEC_EVT: return "ESP_GATTC_EXEC_EVT"; //case ESP_GATTC_GET_CHAR_EVT: // return "ESP_GATTC_GET_CHAR_EVT"; //case ESP_GATTC_GET_DESCR_EVT: // return "ESP_GATTC_GET_DESCR_EVT"; //case ESP_GATTC_GET_INCL_SRVC_EVT: // return "ESP_GATTC_GET_INCL_SRVC_EVT"; case ESP_GATTC_MULT_ADV_DATA_EVT: return "ESP_GATTC_MULT_ADV_DATA_EVT"; case ESP_GATTC_MULT_ADV_DIS_EVT: return "ESP_GATTC_MULT_ADV_DIS_EVT"; case ESP_GATTC_MULT_ADV_ENB_EVT: return "ESP_GATTC_MULT_ADV_ENB_EVT"; case ESP_GATTC_MULT_ADV_UPD_EVT: return "ESP_GATTC_MULT_ADV_UPD_EVT"; case ESP_GATTC_NOTIFY_EVT: return "ESP_GATTC_NOTIFY_EVT"; case ESP_GATTC_OPEN_EVT: return "ESP_GATTC_OPEN_EVT"; case ESP_GATTC_PREP_WRITE_EVT: return "ESP_GATTC_PREP_WRITE_EVT"; case ESP_GATTC_READ_CHAR_EVT: return "ESP_GATTC_READ_CHAR_EVT"; case ESP_GATTC_REG_EVT: return "ESP_GATTC_REG_EVT"; case ESP_GATTC_REG_FOR_NOTIFY_EVT: return "ESP_GATTC_REG_FOR_NOTIFY_EVT"; case ESP_GATTC_SCAN_FLT_CFG_EVT: return "ESP_GATTC_SCAN_FLT_CFG_EVT"; case ESP_GATTC_SCAN_FLT_PARAM_EVT: return "ESP_GATTC_SCAN_FLT_PARAM_EVT"; case ESP_GATTC_SCAN_FLT_STATUS_EVT: return "ESP_GATTC_SCAN_FLT_STATUS_EVT"; case ESP_GATTC_SEARCH_CMPL_EVT: return "ESP_GATTC_SEARCH_CMPL_EVT"; case ESP_GATTC_SEARCH_RES_EVT: return "ESP_GATTC_SEARCH_RES_EVT"; case ESP_GATTC_SRVC_CHG_EVT: return "ESP_GATTC_SRVC_CHG_EVT"; case ESP_GATTC_READ_DESCR_EVT: return "ESP_GATTC_READ_DESCR_EVT"; case ESP_GATTC_UNREG_EVT: return "ESP_GATTC_UNREG_EVT"; case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: return "ESP_GATTC_UNREG_FOR_NOTIFY_EVT"; case ESP_GATTC_WRITE_CHAR_EVT: return "ESP_GATTC_WRITE_CHAR_EVT"; case ESP_GATTC_WRITE_DESCR_EVT: return "ESP_GATTC_WRITE_DESCR_EVT"; #endif default: log_v("Unknown GATT Client event type: %d", eventType); return "Unknown"; } } // gattClientEventTypeToString /** * @brief Return a string representation of a GATT server event code. * @param [in] eventType A GATT server event code. * @return A string representation of the GATT server event code. */ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType) { switch (eventType) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATTS_REG_EVT: return "ESP_GATTS_REG_EVT"; case ESP_GATTS_READ_EVT: return "ESP_GATTS_READ_EVT"; case ESP_GATTS_WRITE_EVT: return "ESP_GATTS_WRITE_EVT"; case ESP_GATTS_EXEC_WRITE_EVT: return "ESP_GATTS_EXEC_WRITE_EVT"; case ESP_GATTS_MTU_EVT: return "ESP_GATTS_MTU_EVT"; case ESP_GATTS_CONF_EVT: return "ESP_GATTS_CONF_EVT"; case ESP_GATTS_UNREG_EVT: return "ESP_GATTS_UNREG_EVT"; case ESP_GATTS_CREATE_EVT: return "ESP_GATTS_CREATE_EVT"; case ESP_GATTS_ADD_INCL_SRVC_EVT: return "ESP_GATTS_ADD_INCL_SRVC_EVT"; case ESP_GATTS_ADD_CHAR_EVT: return "ESP_GATTS_ADD_CHAR_EVT"; case ESP_GATTS_ADD_CHAR_DESCR_EVT: return "ESP_GATTS_ADD_CHAR_DESCR_EVT"; case ESP_GATTS_DELETE_EVT: return "ESP_GATTS_DELETE_EVT"; case ESP_GATTS_START_EVT: return "ESP_GATTS_START_EVT"; case ESP_GATTS_STOP_EVT: return "ESP_GATTS_STOP_EVT"; case ESP_GATTS_CONNECT_EVT: return "ESP_GATTS_CONNECT_EVT"; case ESP_GATTS_DISCONNECT_EVT: return "ESP_GATTS_DISCONNECT_EVT"; case ESP_GATTS_OPEN_EVT: return "ESP_GATTS_OPEN_EVT"; case ESP_GATTS_CANCEL_OPEN_EVT: return "ESP_GATTS_CANCEL_OPEN_EVT"; case ESP_GATTS_CLOSE_EVT: return "ESP_GATTS_CLOSE_EVT"; case ESP_GATTS_LISTEN_EVT: return "ESP_GATTS_LISTEN_EVT"; case ESP_GATTS_CONGEST_EVT: return "ESP_GATTS_CONGEST_EVT"; case ESP_GATTS_RESPONSE_EVT: return "ESP_GATTS_RESPONSE_EVT"; case ESP_GATTS_CREAT_ATTR_TAB_EVT: return "ESP_GATTS_CREAT_ATTR_TAB_EVT"; case ESP_GATTS_SET_ATTR_VAL_EVT: return "ESP_GATTS_SET_ATTR_VAL_EVT"; case ESP_GATTS_SEND_SERVICE_CHANGE_EVT: return "ESP_GATTS_SEND_SERVICE_CHANGE_EVT"; #endif default: return "Unknown"; } } // gattServerEventTypeToString /** * @brief Convert a BLE device type to a string. * @param [in] type The device type. */ const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) { switch (type) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_BT_DEVICE_TYPE_BREDR: return "ESP_BT_DEVICE_TYPE_BREDR"; case ESP_BT_DEVICE_TYPE_BLE: return "ESP_BT_DEVICE_TYPE_BLE"; case ESP_BT_DEVICE_TYPE_DUMO: return "ESP_BT_DEVICE_TYPE_DUMO"; #endif default: return "Unknown"; } } // devTypeToString /** * @brief Dump the GAP event to the log. */ void BLEUtils::dumpGapEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { log_v("Received a GAP event: %s", gapEventToString(event)); switch (event) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT // adv_data_cmpl // - esp_bt_status_t case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { log_v("[status: %d]", param->adv_data_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT // // adv_data_raw_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { log_v("[status: %d]", param->adv_data_raw_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT // ESP_GAP_BLE_ADV_START_COMPLETE_EVT // // adv_start_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { log_v("[status: %d]", param->adv_start_cmpl.status); break; } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT // // adv_stop_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { log_v("[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT // ESP_GAP_BLE_AUTH_CMPL_EVT // // auth_cmpl // - esp_bd_addr_t bd_addr // - bool key_present // - esp_link_key key // - bool success // - uint8_t fail_reason // - esp_bd_addr_type_t addr_type // - esp_bt_dev_type_t dev_type case ESP_GAP_BLE_AUTH_CMPL_EVT: { log_v("[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", BLEAddress(param->ble_security.auth_cmpl.bd_addr).toString().c_str(), param->ble_security.auth_cmpl.key_present, param->ble_security.auth_cmpl.key_type, param->ble_security.auth_cmpl.success, param->ble_security.auth_cmpl.fail_reason, BLEUtils::devTypeToString(param->ble_security.auth_cmpl.dev_type) ); break; } // ESP_GAP_BLE_AUTH_CMPL_EVT // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT // // clear_bond_dev_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: { log_v("[status: %d]", param->clear_bond_dev_cmpl.status); break; } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT // ESP_GAP_BLE_LOCAL_IR_EVT case ESP_GAP_BLE_LOCAL_IR_EVT: { break; } // ESP_GAP_BLE_LOCAL_IR_EVT // ESP_GAP_BLE_LOCAL_ER_EVT case ESP_GAP_BLE_LOCAL_ER_EVT: { break; } // ESP_GAP_BLE_LOCAL_ER_EVT // ESP_GAP_BLE_NC_REQ_EVT case ESP_GAP_BLE_NC_REQ_EVT: { log_v("[bd_addr: %s, passkey: %d]", BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(), param->ble_security.key_notif.passkey); break; } // ESP_GAP_BLE_NC_REQ_EVT // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT // // read_rssi_cmpl // - esp_bt_status_t status // - int8_t rssi // - esp_bd_addr_t remote_addr case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { log_v("[status: %d, rssi: %d, remote_addr: %s]", param->read_rssi_cmpl.status, param->read_rssi_cmpl.rssi, BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str() ); break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT // // scan_param_cmpl. // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { log_v("[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT // ESP_GAP_BLE_SCAN_RESULT_EVT // // scan_rst: // - search_evt // - bda // - dev_type // - ble_addr_type // - ble_evt_type // - rssi // - ble_adv // - flag // - num_resps // - adv_data_len // - scan_rsp_len case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch (param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { log_v("search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d (%s), num_resps: %d, adv_data_len: %d, scan_rsp_len: %d", searchEventTypeToString(param->scan_rst.search_evt), BLEAddress(param->scan_rst.bda).toString().c_str(), devTypeToString(param->scan_rst.dev_type), addressTypeToString(param->scan_rst.ble_addr_type), eventTypeToString(param->scan_rst.ble_evt_type), param->scan_rst.rssi, param->scan_rst.flag, adFlagsToString(param->scan_rst.flag).c_str(), param->scan_rst.num_resps, param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len ); break; } // ESP_GAP_SEARCH_INQ_RES_EVT default: { log_v("search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); break; } } break; } // ESP_GAP_BLE_SCAN_RESULT_EVT // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT // // scan_rsp_data_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { log_v("[status: %d]", param->scan_rsp_data_cmpl.status); break; } // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: { log_v("[status: %d]", param->scan_rsp_data_raw_cmpl.status); break; } // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT // // scan_start_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { log_v("[status: %d]", param->scan_start_cmpl.status); break; } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // // scan_stop_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { log_v("[status: %d]", param->scan_stop_cmpl.status); break; } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT // // update_conn_params // - esp_bt_status_t status // - esp_bd_addr_t bda // - uint16_t min_int // - uint16_t max_int // - uint16_t latency // - uint16_t conn_int // - uint16_t timeout case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { log_v("[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", param->update_conn_params.status, BLEAddress(param->update_conn_params.bda).toString().c_str(), param->update_conn_params.min_int, param->update_conn_params.max_int, param->update_conn_params.latency, param->update_conn_params.conn_int, param->update_conn_params.timeout ); break; } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT // ESP_GAP_BLE_SEC_REQ_EVT case ESP_GAP_BLE_SEC_REQ_EVT: { log_v("[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); break; } // ESP_GAP_BLE_SEC_REQ_EVT #endif default: { log_v("*** dumpGapEvent: Logger not coded ***"); break; } // default } // switch } // dumpGapEvent /** * @brief Decode and dump a GATT client event * * @param [in] event The type of event received. * @param [in] evtParam The data associated with the event. */ void BLEUtils::dumpGattClientEvent( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; log_v("GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); switch (event) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 // ESP_GATTC_CLOSE_EVT // // close: // - esp_gatt_status_t status // - uint16_t conn_id // - esp_bd_addr_t remote_bda // - esp_gatt_conn_reason_t reason case ESP_GATTC_CLOSE_EVT: { log_v("[status: %s, reason:%s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->close.status).c_str(), BLEUtils::gattCloseReasonToString(evtParam->close.reason).c_str(), evtParam->close.conn_id); break; } // ESP_GATTC_CONNECT_EVT // // connect: // - esp_gatt_status_t status // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_CONNECT_EVT: { log_v("[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str() ); break; } // ESP_GATTC_DISCONNECT_EVT // // disconnect: // - esp_gatt_conn_reason_t reason // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { log_v("[reason: %s, conn_id: %d, remote_bda: %s]", BLEUtils::gattCloseReasonToString(evtParam->disconnect.reason).c_str(), evtParam->disconnect.conn_id, BLEAddress(evtParam->disconnect.remote_bda).toString().c_str() ); break; } // ESP_GATTC_DISCONNECT_EVT // ESP_GATTC_GET_CHAR_EVT // // get_char: // - esp_gatt_status_t status // - uin1t6_t conn_id // - esp_gatt_srvc_id_t srvc_id // - esp_gatt_id_t char_id // - esp_gatt_char_prop_t char_prop /* case ESP_GATTC_GET_CHAR_EVT: { // If the status of the event shows that we have a value other than ESP_GATT_OK then the // characteristic fields are not set to a usable value .. so don't try and log them. if (evtParam->get_char.status == ESP_GATT_OK) { std::string description = "Unknown"; if (evtParam->get_char.char_id.uuid.len == ESP_UUID_LEN_16) { description = BLEUtils::gattCharacteristicUUIDToString(evtParam->get_char.char_id.uuid.uuid.uuid16); } log_v("[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]", BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), evtParam->get_char.conn_id, BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str(), gattIdToString(evtParam->get_char.char_id).c_str(), description.c_str(), BLEUtils::characteristicPropertiesToString(evtParam->get_char.char_prop).c_str() ); } else { log_v("[status: %s, conn_id: %d, srvc_id: %s]", BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), evtParam->get_char.conn_id, BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str() ); } break; } // ESP_GATTC_GET_CHAR_EVT */ // ESP_GATTC_NOTIFY_EVT // // notify // uint16_t conn_id // esp_bd_addr_t remote_bda // handle handle // uint16_t value_len // uint8_t* value // bool is_notify // case ESP_GATTC_NOTIFY_EVT: { log_v("[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", evtParam->notify.conn_id, BLEAddress(evtParam->notify.remote_bda).toString().c_str(), evtParam->notify.handle, evtParam->notify.handle, evtParam->notify.value_len, evtParam->notify.is_notify ); break; } // ESP_GATTC_OPEN_EVT // // open: // - esp_gatt_status_t status // - uint16_t conn_id // - esp_bd_addr_t remote_bda // - uint16_t mtu // case ESP_GATTC_OPEN_EVT: { log_v("[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]", BLEUtils::gattStatusToString(evtParam->open.status).c_str(), evtParam->open.conn_id, BLEAddress(evtParam->open.remote_bda).toString().c_str(), evtParam->open.mtu); break; } // ESP_GATTC_OPEN_EVT // ESP_GATTC_READ_CHAR_EVT // // Callback to indicate that requested data that we wanted to read is now available. // // read: // esp_gatt_status_t status // uint16_t conn_id // uint16_t handle // uint8_t* value // uint16_t value_type // uint16_t value_len case ESP_GATTC_READ_CHAR_EVT: { log_v("[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", BLEUtils::gattStatusToString(evtParam->read.status).c_str(), evtParam->read.conn_id, evtParam->read.handle, evtParam->read.handle, evtParam->read.value_len ); if (evtParam->read.status == ESP_GATT_OK) { GeneralUtils::hexDump(evtParam->read.value, evtParam->read.value_len); /* char* pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); log_v("value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); free(pHexData); */ } break; } // ESP_GATTC_READ_CHAR_EVT // ESP_GATTC_REG_EVT // // reg: // - esp_gatt_status_t status // - uint16_t app_id case ESP_GATTC_REG_EVT: { log_v("[status: %s, app_id: 0x%x]", BLEUtils::gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; } // ESP_GATTC_REG_EVT // ESP_GATTC_REG_FOR_NOTIFY_EVT // // reg_for_notify: // - esp_gatt_status_t status // - uint16_t handle case ESP_GATTC_REG_FOR_NOTIFY_EVT: { log_v("[status: %s, handle: %d 0x%.2x]", BLEUtils::gattStatusToString(evtParam->reg_for_notify.status).c_str(), evtParam->reg_for_notify.handle, evtParam->reg_for_notify.handle ); break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT // ESP_GATTC_SEARCH_CMPL_EVT // // search_cmpl: // - esp_gatt_status_t status // - uint16_t conn_id case ESP_GATTC_SEARCH_CMPL_EVT: { log_v("[status: %s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(), evtParam->search_cmpl.conn_id); break; } // ESP_GATTC_SEARCH_CMPL_EVT // ESP_GATTC_SEARCH_RES_EVT // // search_res: // - uint16_t conn_id // - uint16_t start_handle // - uint16_t end_handle // - esp_gatt_id_t srvc_id case ESP_GATTC_SEARCH_RES_EVT: { log_v("[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", evtParam->search_res.conn_id, evtParam->search_res.start_handle, evtParam->search_res.start_handle, evtParam->search_res.end_handle, evtParam->search_res.end_handle, gattIdToString(evtParam->search_res.srvc_id).c_str()); break; } // ESP_GATTC_SEARCH_RES_EVT // ESP_GATTC_WRITE_CHAR_EVT // // write: // - esp_gatt_status_t status // - uint16_t conn_id // - uint16_t handle // - uint16_t offset case ESP_GATTC_WRITE_CHAR_EVT: { log_v("[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]", BLEUtils::gattStatusToString(evtParam->write.status).c_str(), evtParam->write.conn_id, evtParam->write.handle, evtParam->write.handle, evtParam->write.offset ); break; } // ESP_GATTC_WRITE_CHAR_EVT #endif default: break; } } // dumpGattClientEvent /** * @brief Dump the details of a GATT server event. * A GATT Server event is a callback received from the BLE subsystem when we are acting as a BLE * server. The callback indicates the type of event in the `event` field. The `evtParam` is a * union of structures where we can use the `event` to indicate which of the structures has been * populated and hence is valid. * * @param [in] event The event type that was posted. * @param [in] evtParam A union of structures only one of which is populated. */ void BLEUtils::dumpGattServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam) { log_v("GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); switch (event) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATTS_ADD_CHAR_DESCR_EVT: { log_v("[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char_descr.status).c_str(), evtParam->add_char_descr.attr_handle, evtParam->add_char_descr.attr_handle, evtParam->add_char_descr.service_handle, evtParam->add_char_descr.service_handle, BLEUUID(evtParam->add_char_descr.descr_uuid).toString().c_str()); break; } // ESP_GATTS_ADD_CHAR_DESCR_EVT case ESP_GATTS_ADD_CHAR_EVT: { if (evtParam->add_char.status == ESP_GATT_OK) { log_v("[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char.status).c_str(), evtParam->add_char.attr_handle, evtParam->add_char.attr_handle, evtParam->add_char.service_handle, evtParam->add_char.service_handle, BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); } else { log_e("[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char.status).c_str(), evtParam->add_char.attr_handle, evtParam->add_char.attr_handle, evtParam->add_char.service_handle, evtParam->add_char.service_handle, BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); } break; } // ESP_GATTS_ADD_CHAR_EVT // ESP_GATTS_CONF_EVT // // conf: // - esp_gatt_status_t status – The status code. // - uint16_t conn_id – The connection used. case ESP_GATTS_CONF_EVT: { log_v("[status: %s, conn_id: 0x%.2x]", gattStatusToString(evtParam->conf.status).c_str(), evtParam->conf.conn_id); break; } // ESP_GATTS_CONF_EVT case ESP_GATTS_CONGEST_EVT: { log_v("[conn_id: %d, congested: %d]", evtParam->congest.conn_id, evtParam->congest.congested); break; } // ESP_GATTS_CONGEST_EVT case ESP_GATTS_CONNECT_EVT: { log_v("[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; } // ESP_GATTS_CONNECT_EVT case ESP_GATTS_CREATE_EVT: { log_v("[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", gattStatusToString(evtParam->create.status).c_str(), evtParam->create.service_handle, evtParam->create.service_handle, gattServiceIdToString(evtParam->create.service_id).c_str()); break; } // ESP_GATTS_CREATE_EVT case ESP_GATTS_DISCONNECT_EVT: { log_v("[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; } // ESP_GATTS_DISCONNECT_EVT // ESP_GATTS_EXEC_WRITE_EVT // exec_write: // - uint16_t conn_id // - uint32_t trans_id // - esp_bd_addr_t bda // - uint8_t exec_write_flag case ESP_GATTS_EXEC_WRITE_EVT: { char* pWriteFlagText; switch (evtParam->exec_write.exec_write_flag) { case ESP_GATT_PREP_WRITE_EXEC: { pWriteFlagText = (char*) "WRITE"; break; } case ESP_GATT_PREP_WRITE_CANCEL: { pWriteFlagText = (char*) "CANCEL"; break; } default: pWriteFlagText = (char*) ""; break; } log_v("[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]", evtParam->exec_write.conn_id, evtParam->exec_write.trans_id, BLEAddress(evtParam->exec_write.bda).toString().c_str(), evtParam->exec_write.exec_write_flag, pWriteFlagText); break; } // ESP_GATTS_DISCONNECT_EVT case ESP_GATTS_MTU_EVT: { log_v("[conn_id: %d, mtu: %d]", evtParam->mtu.conn_id, evtParam->mtu.mtu); break; } // ESP_GATTS_MTU_EVT case ESP_GATTS_READ_EVT: { log_v("[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]", evtParam->read.conn_id, evtParam->read.trans_id, BLEAddress(evtParam->read.bda).toString().c_str(), evtParam->read.handle, evtParam->read.is_long, evtParam->read.need_rsp); break; } // ESP_GATTS_READ_EVT case ESP_GATTS_RESPONSE_EVT: { log_v("[status: %s, handle: 0x%.2x]", gattStatusToString(evtParam->rsp.status).c_str(), evtParam->rsp.handle); break; } // ESP_GATTS_RESPONSE_EVT case ESP_GATTS_REG_EVT: { log_v("[status: %s, app_id: %d]", gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; } // ESP_GATTS_REG_EVT // ESP_GATTS_START_EVT // // start: // - esp_gatt_status_t status // - uint16_t service_handle case ESP_GATTS_START_EVT: { log_v("[status: %s, service_handle: 0x%.2x]", gattStatusToString(evtParam->start.status).c_str(), evtParam->start.service_handle); break; } // ESP_GATTS_START_EVT // ESP_GATTS_WRITE_EVT // // write: // - uint16_t conn_id – The connection id. // - uint16_t trans_id – The transfer id. // - esp_bd_addr_t bda – The address of the partner. // - uint16_t handle – The attribute handle. // - uint16_t offset – The offset of the currently received within the whole value. // - bool need_rsp – Do we need a response? // - bool is_prep – Is this a write prepare? If set, then this is to be considered part of the received value and not the whole value. A subsequent ESP_GATTS_EXEC_WRITE will mark the total. // - uint16_t len – The length of the incoming value part. // - uint8_t* value – The data for this value part. case ESP_GATTS_WRITE_EVT: { log_v("[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]", evtParam->write.conn_id, evtParam->write.trans_id, BLEAddress(evtParam->write.bda).toString().c_str(), evtParam->write.handle, evtParam->write.offset, evtParam->write.need_rsp, evtParam->write.is_prep, evtParam->write.len); char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); log_v("[Data: %s]", pHex); free(pHex); break; } // ESP_GATTS_WRITE_EVT #endif default: log_v("dumpGattServerEvent: *** NOT CODED ***"); break; } } // dumpGattServerEvent /** * @brief Convert a BLE event type to a string. * @param [in] eventType The event type. * @return The event type as a string. */ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { switch (eventType) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_BLE_EVT_CONN_ADV: return "ESP_BLE_EVT_CONN_ADV"; case ESP_BLE_EVT_CONN_DIR_ADV: return "ESP_BLE_EVT_CONN_DIR_ADV"; case ESP_BLE_EVT_DISC_ADV: return "ESP_BLE_EVT_DISC_ADV"; case ESP_BLE_EVT_NON_CONN_ADV: return "ESP_BLE_EVT_NON_CONN_ADV"; case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; #endif default: log_v("Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; } } // eventTypeToString /** * @brief Convert a BT GAP event type to a string representation. * @param [in] eventType The type of event. * @return A string representation of the event type. */ const char* BLEUtils::gapEventToString(uint32_t eventType) { switch (eventType) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT"; case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT"; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_START_COMPLETE_EVT"; case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /* !< When stop adv complete, the event comes */ return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */ return "ESP_GAP_BLE_AUTH_CMPL_EVT"; case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT"; case ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT"; case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */ return "ESP_GAP_BLE_KEY_EVT"; case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ return "ESP_GAP_BLE_LOCAL_IR_EVT"; case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ return "ESP_GAP_BLE_LOCAL_ER_EVT"; case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */ return "ESP_GAP_BLE_NC_REQ_EVT"; case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ return "ESP_GAP_BLE_OOB_REQ_EVT"; case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"; case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ return "ESP_GAP_BLE_PASSKEY_REQ_EVT"; case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_RESULT_EVT: return "ESP_GAP_BLE_SCAN_RESULT_EVT"; case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT"; case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ return "ESP_GAP_BLE_SEC_REQ_EVT"; case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: return "ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT"; case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT"; #endif default: log_v("gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } } // gapEventToString std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { const characteristicMap_t* p = g_characteristicsMappings; while (strlen(p->name) > 0) { if (p->assignedNumber == characteristicUUID) { return std::string(p->name); } p++; } return "Unknown"; } // gattCharacteristicUUIDToString /** * @brief Given the UUID for a BLE defined descriptor, return its string representation. * @param [in] descriptorUUID UUID of the descriptor to be returned as a string. * @return The string representation of a descriptor UUID. */ std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { gattdescriptor_t* p = (gattdescriptor_t*) g_descriptor_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == descriptorUUID) { return std::string(p->name); } p++; } return ""; } // gattDescriptorUUIDToString /** * @brief Return a string representation of an esp_gattc_service_elem_t. * @return A string representation of an esp_gattc_service_elem_t. */ std::string BLEUtils::gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement) { std::stringstream ss; ss << "[uuid: " << BLEUUID(pGATTCServiceElement->uuid).toString() << ", start_handle: " << pGATTCServiceElement->start_handle << " 0x" << std::hex << pGATTCServiceElement->start_handle << ", end_handle: " << std::dec << pGATTCServiceElement->end_handle << " 0x" << std::hex << pGATTCServiceElement->end_handle << "]"; return ss.str(); } // gattcServiceElementToString /** * @brief Convert an esp_gatt_srvc_id_t to a string. */ std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { return gattIdToString(srvcId.id); } // gattServiceIdToString std::string BLEUtils::gattServiceToString(uint32_t serviceId) { gattService_t* p = (gattService_t*) g_gattServices; while (strlen(p->name) > 0) { if (p->assignedNumber == serviceId) { return std::string(p->name); } p++; } return "Unknown"; } // gattServiceToString /** * @brief Convert a GATT status to a string. * * @param [in] status The status to convert. * @return A string representation of the status. */ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { switch (status) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATT_OK: return "ESP_GATT_OK"; case ESP_GATT_INVALID_HANDLE: return "ESP_GATT_INVALID_HANDLE"; case ESP_GATT_READ_NOT_PERMIT: return "ESP_GATT_READ_NOT_PERMIT"; case ESP_GATT_WRITE_NOT_PERMIT: return "ESP_GATT_WRITE_NOT_PERMIT"; case ESP_GATT_INVALID_PDU: return "ESP_GATT_INVALID_PDU"; case ESP_GATT_INSUF_AUTHENTICATION: return "ESP_GATT_INSUF_AUTHENTICATION"; case ESP_GATT_REQ_NOT_SUPPORTED: return "ESP_GATT_REQ_NOT_SUPPORTED"; case ESP_GATT_INVALID_OFFSET: return "ESP_GATT_INVALID_OFFSET"; case ESP_GATT_INSUF_AUTHORIZATION: return "ESP_GATT_INSUF_AUTHORIZATION"; case ESP_GATT_PREPARE_Q_FULL: return "ESP_GATT_PREPARE_Q_FULL"; case ESP_GATT_NOT_FOUND: return "ESP_GATT_NOT_FOUND"; case ESP_GATT_NOT_LONG: return "ESP_GATT_NOT_LONG"; case ESP_GATT_INSUF_KEY_SIZE: return "ESP_GATT_INSUF_KEY_SIZE"; case ESP_GATT_INVALID_ATTR_LEN: return "ESP_GATT_INVALID_ATTR_LEN"; case ESP_GATT_ERR_UNLIKELY: return "ESP_GATT_ERR_UNLIKELY"; case ESP_GATT_INSUF_ENCRYPTION: return "ESP_GATT_INSUF_ENCRYPTION"; case ESP_GATT_UNSUPPORT_GRP_TYPE: return "ESP_GATT_UNSUPPORT_GRP_TYPE"; case ESP_GATT_INSUF_RESOURCE: return "ESP_GATT_INSUF_RESOURCE"; case ESP_GATT_NO_RESOURCES: return "ESP_GATT_NO_RESOURCES"; case ESP_GATT_INTERNAL_ERROR: return "ESP_GATT_INTERNAL_ERROR"; case ESP_GATT_WRONG_STATE: return "ESP_GATT_WRONG_STATE"; case ESP_GATT_DB_FULL: return "ESP_GATT_DB_FULL"; case ESP_GATT_BUSY: return "ESP_GATT_BUSY"; case ESP_GATT_ERROR: return "ESP_GATT_ERROR"; case ESP_GATT_CMD_STARTED: return "ESP_GATT_CMD_STARTED"; case ESP_GATT_ILLEGAL_PARAMETER: return "ESP_GATT_ILLEGAL_PARAMETER"; case ESP_GATT_PENDING: return "ESP_GATT_PENDING"; case ESP_GATT_AUTH_FAIL: return "ESP_GATT_AUTH_FAIL"; case ESP_GATT_MORE: return "ESP_GATT_MORE"; case ESP_GATT_INVALID_CFG: return "ESP_GATT_INVALID_CFG"; case ESP_GATT_SERVICE_STARTED: return "ESP_GATT_SERVICE_STARTED"; case ESP_GATT_ENCRYPED_NO_MITM: return "ESP_GATT_ENCRYPED_NO_MITM"; case ESP_GATT_NOT_ENCRYPTED: return "ESP_GATT_NOT_ENCRYPTED"; case ESP_GATT_CONGESTED: return "ESP_GATT_CONGESTED"; case ESP_GATT_DUP_REG: return "ESP_GATT_DUP_REG"; case ESP_GATT_ALREADY_OPEN: return "ESP_GATT_ALREADY_OPEN"; case ESP_GATT_CANCEL: return "ESP_GATT_CANCEL"; case ESP_GATT_STACK_RSP: return "ESP_GATT_STACK_RSP"; case ESP_GATT_APP_RSP: return "ESP_GATT_APP_RSP"; case ESP_GATT_UNKNOWN_ERROR: return "ESP_GATT_UNKNOWN_ERROR"; case ESP_GATT_CCC_CFG_ERR: return "ESP_GATT_CCC_CFG_ERR"; case ESP_GATT_PRC_IN_PROGRESS: return "ESP_GATT_PRC_IN_PROGRESS"; case ESP_GATT_OUT_OF_RANGE: return "ESP_GATT_OUT_OF_RANGE"; #endif default: return "Unknown"; } } // gattStatusToString std::string BLEUtils::getMember(uint32_t memberId) { member_t* p = (member_t*) members_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == memberId) { return std::string(p->name); } p++; } return "Unknown"; } /** * @brief convert a GAP search event to a string. * @param [in] searchEvt * @return The search event type as a string. */ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { switch (searchEvt) { #if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GAP_SEARCH_INQ_RES_EVT: return "ESP_GAP_SEARCH_INQ_RES_EVT"; case ESP_GAP_SEARCH_INQ_CMPL_EVT: return "ESP_GAP_SEARCH_INQ_CMPL_EVT"; case ESP_GAP_SEARCH_DISC_RES_EVT: return "ESP_GAP_SEARCH_DISC_RES_EVT"; case ESP_GAP_SEARCH_DISC_BLE_RES_EVT: return "ESP_GAP_SEARCH_DISC_BLE_RES_EVT"; case ESP_GAP_SEARCH_DISC_CMPL_EVT: return "ESP_GAP_SEARCH_DISC_CMPL_EVT"; case ESP_GAP_SEARCH_DI_DISC_CMPL_EVT: return "ESP_GAP_SEARCH_DI_DISC_CMPL_EVT"; case ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT: return "ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT"; #endif default: log_v("Unknown event type: 0x%x", searchEvt); return "Unknown event type"; } } // searchEventTypeToString #endif // CONFIG_BT_ENABLED