From 8dafeddaa9ee5c8a264302650e5ca0e29cd4524d Mon Sep 17 00:00:00 2001 From: kolban Date: Wed, 27 Sep 2017 11:25:57 -0500 Subject: [PATCH 001/310] Addition of extra event types for #89 --- cpp_utils/BLEUtils.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index eb8597f7..a6bab96f 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1185,8 +1185,24 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_STOP_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"; + case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; + case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_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_READ_RSSI_COMPLETE_EVT: + return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; default: - ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type 0x%x", eventType); + ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } } // gapEventToString From 5dcf485fe34c80fcf2d1adc8298ad53ac48f9c78 Mon Sep 17 00:00:00 2001 From: kolban Date: Wed, 27 Sep 2017 11:35:22 -0500 Subject: [PATCH 002/310] Log added for #89 --- cpp_utils/BLEUtils.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index a6bab96f..dca4f423 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -635,6 +635,23 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + + // + // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT + // + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { + ESP_LOGD(LOG_TAG, "[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 + ); + } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT + + default: { ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; From 7c136cbfd5d21e36f0fd96395cb68c01a96d1552 Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Thu, 28 Sep 2017 12:18:01 +0300 Subject: [PATCH 003/310] Some typecasting to make things easier for the user --- cpp_utils/WebServer.cpp | 3 +++ cpp_utils/WebServer.h | 1 + 2 files changed, 4 insertions(+) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index 2b9f51ca..2c975939 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -478,6 +478,9 @@ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { m_nc->flags |= MG_F_SEND_AND_CLOSE; } // sendData +void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { + sendData((uint8_t*) pData, length); +} // sendData /** * @brief Set the headers to be sent in the HTTP response. diff --git a/cpp_utils/WebServer.h b/cpp_utils/WebServer.h index 39d55e38..7bb1e585 100644 --- a/cpp_utils/WebServer.h +++ b/cpp_utils/WebServer.h @@ -55,6 +55,7 @@ class WebServer { void setHeaders(std::map&& headers); void sendData(const std::string& data); void sendData(const uint8_t* pData, size_t length); + void sendData(const char* pData, size_t length); const std::string& getRootPath() const; void setRootPath(const std::string& path); void setRootPath(std::string&& path); From 447a52e0d2fcd01645e64966b12c92384b4e11c4 Mon Sep 17 00:00:00 2001 From: kolban Date: Thu, 28 Sep 2017 08:43:34 -0500 Subject: [PATCH 004/310] Insertions for #91 --- cpp_utils/BLECharacteristic.cpp | 5 +- cpp_utils/BLEClient.cpp | 25 ++- cpp_utils/BLEClient.h | 1 + cpp_utils/BLERemoteCharacteristic.cpp | 4 +- cpp_utils/BLERemoteService.cpp | 3 +- cpp_utils/BLEUtils.cpp | 257 +++++++++++++++++++++++--- cpp_utils/WiFi.cpp | 110 ++++++----- cpp_utils/WiFi.h | 18 +- cpp_utils/WiFiEventHandler.cpp | 137 ++++++++------ cpp_utils/WiFiEventHandler.h | 8 +- 10 files changed, 426 insertions(+), 142 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index f5b82006..cdc2200e 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -131,7 +131,6 @@ void BLECharacteristic::executeCreate(BLEService* pService) { } // executeCreate - /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. @@ -141,6 +140,7 @@ BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); } // getDescriptorByUUID + /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. @@ -274,7 +274,7 @@ void BLECharacteristic::handleGATTServerEvent( ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", getHandle(), getUUID().toString().c_str()); - char *pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); + char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData); free(pHexData); @@ -422,6 +422,7 @@ void BLECharacteristic::handleGATTServerEvent( } // handleGATTServerEvent + /** * @brief Send an indication. * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 3b2ca2c9..d518d531 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -48,13 +48,28 @@ BLEClient::BLEClient() { m_haveServices = false; } // BLEClient + +/** + * @brief Destructor. + */ +BLEClient::~BLEClient() { + // We may have allocated service references associated with this client. Before we are finished + // with the client, we must release resources. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); +} // ~BLEClient + + /** - * @brief Connect to the partner. + * @brief Connect to the partner (BLE Server). * @param [in] address The address of the partner. * @return True on success. */ bool BLEClient::connect(BLEAddress address) { ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + // We need the connection handle that we get from registering the application. We register the app // and then block on its completion. When the event has arrived, we will have the handle. m_semaphoreRegEvt.take("connect"); @@ -63,10 +78,12 @@ bool BLEClient::connect(BLEAddress address) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; } + m_semaphoreRegEvt.wait("connect"); m_peerAddress = address; + // Perform the open connection request against the target BLE Server. m_semaphoreOpenEvt.take("connect"); errRc = ::esp_ble_gattc_open( getGattcIf(), @@ -243,7 +260,7 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) { ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); return myPair.second; } - } + } // End of each of the services. ESP_LOGD(LOG_TAG, "<< getService: not found"); return nullptr; } // getService @@ -268,7 +285,7 @@ std::map* BLEClient::getServices() { esp_err_t errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), - NULL // Filter UUID + nullptr // Filter UUID ); m_semaphoreSearchCmplEvt.take("getServices"); if (errRc != ESP_OK) { @@ -300,7 +317,7 @@ std::string BLEClient::toString() { ss << "\nServices:\n"; for (auto &myPair : m_servicesMap) { ss << myPair.second->toString() << "\n"; - // myPair.second is the value + // myPair.second is the value } return ss.str(); } // toString diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index a3f51bcc..494f51dd 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -28,6 +28,7 @@ class BLEClientCallbacks; class BLEClient { public: BLEClient(); + ~BLEClient(); bool connect(BLEAddress address); void disconnect(); BLEAddress getPeerAddress(); diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 88db2e2a..fdd5b1ff 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -446,10 +446,12 @@ void BLERemoteCharacteristic::registerForNotify( * @return N/A. */ void BLERemoteCharacteristic::removeDescriptors() { + // Iterate through all the descriptors releasing their storage and erasing them from the map. for (auto &myPair : m_descriptorMap) { + m_descriptorMap.erase(myPair.first); delete myPair.second; } - m_descriptorMap.empty(); + m_descriptorMap.clear(); // Technically not neeeded, but just to be sure. } // removeCharacteristics diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index a6cf0566..b9fd29de 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -301,8 +301,9 @@ BLEUUID BLERemoteService::getUUID() { void BLERemoteService::removeCharacteristics() { for (auto &myPair : m_characteristicMap) { delete myPair.second; + m_characteristicMap.erase(myPair.first); } - m_characteristicMap.empty(); + m_characteristicMap.clear(); // Clear the map } // removeCharacteristics diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index dca4f423..4c088d66 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -6,10 +6,10 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "BLEAddress.h" +#include "BLEClient.h" #include "BLEUtils.h" #include "BLEUUID.h" -#include "BLEClient.h" -#include "BLEAddress.h" #include "GeneralUtils.h" #include @@ -24,7 +24,7 @@ #include #include -static const char* LOG_TAG = "BLEUtils"; +static const char* LOG_TAG = "BLEUtils"; // Tag for logging. /* static std::map g_addressMap; @@ -32,13 +32,227 @@ static std::map g_connIdMap; */ typedef struct { - uint32_t assignedNumber; + uint32_t assignedNumber; std::string name; } characteristicMap_t; -static characteristicMap_t g_characteristicsMappings[] = { - {0x2a00, "Device Name"}, - {0x2a01, "Appearance"}, +static const characteristicMap_t g_characteristicsMappings[] = { + {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"}, {0, ""} }; @@ -48,7 +262,7 @@ static characteristicMap_t g_characteristicsMappings[] = { typedef struct { std::string name; std::string type; - uint32_t assignedNumber; + uint32_t assignedNumber; } gattService_t; @@ -94,6 +308,7 @@ static const gattService_t g_gattServices[] = { {"", "", 0 } }; + /** * @brief Convert characteristic properties into a string representation. * @param [in] prop Characteristic properties. @@ -137,7 +352,7 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { case BLE_ADDR_TYPE_RPA_RANDOM: return "BLE_ADDR_TYPE_RPA_RANDOM"; default: - return "Unknown addr_t"; + return "Unknown esp_ble_addr_type_t"; } } // addressTypeToString @@ -232,13 +447,12 @@ esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, * @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) { +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) { @@ -640,7 +854,7 @@ void BLEUtils::dumpGapEvent( // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT // case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d", + ESP_LOGD(LOG_TAG, "[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, @@ -649,6 +863,7 @@ void BLEUtils::dumpGapEvent( param->update_conn_params.conn_int, param->update_conn_params.timeout ); + break; } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT @@ -667,9 +882,9 @@ void BLEUtils::dumpGapEvent( * @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_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; ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); @@ -938,9 +1153,9 @@ void BLEUtils::dumpGattClientEvent( * @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) { + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* evtParam) { ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); switch(event) { @@ -1119,7 +1334,7 @@ void BLEUtils::dumpGattServerEvent( evtParam->write.need_rsp, evtParam->write.is_prep, evtParam->write.len); - char *pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); + char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); ESP_LOGD(LOG_TAG, "[Data: %s]", pHex); free(pHex); break; @@ -1226,7 +1441,7 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { - characteristicMap_t *p = g_characteristicsMappings; + const characteristicMap_t *p = g_characteristicsMappings; while (p->name.length() > 0) { if (p->assignedNumber == characteristicUUID) { return p->name; @@ -1241,7 +1456,7 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID * @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::string BLEUtils::gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement) { std::stringstream ss; ss << "[uuid: " << BLEUUID(pGATTCServiceElement->uuid).toString() << diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index d3c44a9c..cc556ec1 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -28,7 +28,7 @@ #include -static char tag[]= "WiFi"; +static const char* LOG_TAG = "WiFi"; /* @@ -48,16 +48,20 @@ WiFi::WiFi() : ip(0) , gw(0) , netmask(0) - , wifiEventHandler(nullptr) + , m_wifiEventHandler(nullptr) { - wifiEventHandler = new WiFiEventHandler(); -} + ::nvs_flash_init(); + wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&config); + ::tcpip_adapter_init(); + m_wifiEventHandler = new WiFiEventHandler(); +} // WiFi /** * @brief Deletes the event handler that was used by the class */ WiFi::~WiFi() { - delete wifiEventHandler; + delete m_wifiEventHandler; } /** @@ -88,7 +92,7 @@ void WiFi::addDNSServer(const char* ip) { } // addDNSServer void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(tag, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); ::dns_setserver(m_dnsCount, &ip); m_dnsCount++; m_dnsCount %= 2; @@ -121,7 +125,7 @@ void WiFi::setDNSServer(int numdns, const char* ip) { } // setDNSServer void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(tag, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); ::dns_setserver(numdns, &ip); } // setDNSServer @@ -135,44 +139,45 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @return N/A. */ void WiFi::connectAP(const std::string& ssid, const std::string& password){ - ::nvs_flash_init(); - ::tcpip_adapter_init(); - if (ip != 0 && gw != 0 && netmask != 0) { - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client + ESP_LOGD(LOG_TAG, ">> connectAP"); - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; + if (ip != 0 && gw != 0 && netmask != 0) { + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } - ESP_ERROR_CHECK( esp_event_loop_init(wifiEventHandler->getEventHandler(), wifiEventHandler)); - ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); - wifi_config_t sta_config; - ::memset(&sta_config, 0, sizeof(sta_config)); - ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); - ::memcpy(sta_config.sta.password, password.data(), password.size()); - sta_config.sta.bssid_set = 0; - ESP_ERROR_CHECK(::esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - ESP_ERROR_CHECK(::esp_wifi_start()); - - ESP_ERROR_CHECK(::esp_wifi_connect()); + + ESP_ERROR_CHECK( esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); + ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); + wifi_config_t sta_config; + ::memset(&sta_config, 0, sizeof(sta_config)); + ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); + ::memcpy(sta_config.sta.password, password.data(), password.size()); + sta_config.sta.bssid_set = 0; + ESP_ERROR_CHECK(::esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + ESP_ERROR_CHECK(::esp_wifi_start()); + + ESP_ERROR_CHECK(::esp_wifi_connect()); + ESP_LOGD(LOG_TAG, "<< connectAP"); } // connectAP /** * @brief Dump diagnostics to the log. */ void WiFi::dump() { - ESP_LOGD(tag, "WiFi Dump"); - ESP_LOGD(tag, "---------"); + ESP_LOGD(LOG_TAG, "WiFi Dump"); + ESP_LOGD(LOG_TAG, "---------"); char ipAddrStr[30]; ip_addr_t ip = ::dns_getserver(0); inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); - ESP_LOGD(tag, "DNS Server[0]: %s", ipAddrStr); + ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); } // dump /** @@ -227,10 +232,10 @@ struct in_addr WiFi::getHostByName(const char* hostName) { struct hostent *he = gethostbyname(hostName); if (he == nullptr) { retAddr.s_addr = 0; - ESP_LOGD(tag, "Unable to resolve %s - %d", hostName, h_errno); + ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); } else { retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(tag, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); } return retAddr; } // getHostByName @@ -306,7 +311,7 @@ std::string WiFi::getStaSSID() { std::vector WiFi::scan() { ::nvs_flash_init(); ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(wifiEventHandler->getEventHandler(), wifiEventHandler)); + ESP_ERROR_CHECK(esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); @@ -317,11 +322,11 @@ std::vector WiFi::scan() { conf.show_hidden = true; esp_err_t rc = ::esp_wifi_scan_start(&conf, true); if (rc != ESP_OK) { - ESP_LOGE(tag, "esp_wifi_scan_start: %d", rc); + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); } uint16_t apCount; rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(tag, "Count of found access points: %d", apCount); + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); wifi_ap_record_t *list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list)); @@ -348,7 +353,7 @@ std::vector WiFi::scan() { void WiFi::startAP(const std::string& ssid, const std::string& password) { ::nvs_flash_init(); ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(wifiEventHandler->getEventHandler(), wifiEventHandler)); + ESP_ERROR_CHECK(esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); @@ -368,6 +373,15 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { } // startAP +/** + * @brief Set the event handler to use to process detected events. + * @param[in] wifiEventHandler The class that will be used to process events. + */ +void WiFi::setWifiEventHandler(WiFiEventHandler *wifiEventHandler) { + this->m_wifiEventHandler = wifiEventHandler; +} // setWifiEventHandler + + /** * @brief Set the IP info and enable DHCP if ip != 0. If called with ip == 0 then DHCP is enabled. * If called with bad values it will do nothing. @@ -389,6 +403,8 @@ void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::st setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); } // setIPInfo + + void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { uint32_t new_ip; uint32_t new_gw; @@ -405,15 +421,22 @@ void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { setIPInfo(new_ip, new_gw, new_netmask); } // setIPInfo + +/** + * @brief Set the IP Info based on the IP address, gateway and netmask. + * @param [in] ip The IP address of our ESP32. + * @param [in] gw The gateway we should use. + * @param [in] netmask Our TCP/IP netmask value. + */ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { - this->ip = ip; - this->gw = gw; + this->ip = ip; + this->gw = gw; this->netmask = netmask; if(ip != 0 && gw != 0 && netmask != 0) { tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; ipInfo.netmask.addr = netmask; ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); @@ -422,7 +445,8 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { ip = 0; ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); } -} +} // setIPInfo + /** * @brief Return a string representation of the WiFi access point record. diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index b7499c48..1299f9f1 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -70,9 +70,9 @@ class WiFiAPRecord { std::string toString(); private: - uint8_t m_bssid[6]; - int8_t m_rssi; - std::string m_ssid; + uint8_t m_bssid[6]; + int8_t m_rssi; + std::string m_ssid; wifi_auth_mode_t m_authMode; }; @@ -106,7 +106,7 @@ class WiFi { uint32_t ip; uint32_t gw; uint32_t netmask; - WiFiEventHandler *wifiEventHandler; + WiFiEventHandler *m_wifiEventHandler; public: WiFi(); @@ -133,15 +133,7 @@ class WiFi { void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); void setIPInfo(const char* ip, const char* gw, const char* netmask); void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - - /** - * Set the event handler to use to process detected events. - * @param[in] wifiEventHandler The class that will be used to process events. - */ - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler) { - delete this->wifiEventHandler; - this->wifiEventHandler = wifiEventHandler; - } + void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); private: uint8_t m_dnsCount=0; //char *m_dnsServer = nullptr; diff --git a/cpp_utils/WiFiEventHandler.cpp b/cpp_utils/WiFiEventHandler.cpp index 59519ed4..405058ad 100644 --- a/cpp_utils/WiFiEventHandler.cpp +++ b/cpp_utils/WiFiEventHandler.cpp @@ -13,7 +13,7 @@ #include #include "sdkconfig.h" -static char tag[] = "WiFiEventHandler"; +static const char* LOG_TAG = "WiFiEventHandler"; /** * @brief The entry point into the event handler. @@ -23,54 +23,74 @@ static char tag[] = "WiFiEventHandler"; * @param [in] event * @return ESP_OK if the event was handled otherwise an error. */ -esp_err_t WiFiEventHandler::eventHandler(void *ctx, system_event_t *event) { - ESP_LOGD(tag, "eventHandler called"); - WiFiEventHandler *pWiFiEventHandler = (WiFiEventHandler *)ctx; - if (ctx == nullptr) { - ESP_LOGD(tag, "No context"); - return ESP_OK; +esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { + ESP_LOGD(LOG_TAG, ">> eventHandler called: ctx=0x%x, event=0x%x", (uint32_t)ctx, (uint32_t)event); + WiFiEventHandler *pWiFiEventHandler = (WiFiEventHandler *)ctx; + if (ctx == nullptr) { + ESP_LOGD(LOG_TAG, "No context"); + return ESP_OK; + } + + esp_err_t rc = ESP_OK; + switch(event->event_id) { + case SYSTEM_EVENT_AP_START: { + rc = pWiFiEventHandler->apStart(); + break; + } + case SYSTEM_EVENT_AP_STOP: { + rc = pWiFiEventHandler->apStop(); + break; + } + case SYSTEM_EVENT_STA_CONNECTED: { + rc = pWiFiEventHandler->staConnected(); + break; + } + + case SYSTEM_EVENT_STA_DISCONNECTED: { + rc = pWiFiEventHandler->staDisconnected(); + break; + } + + case SYSTEM_EVENT_STA_GOT_IP: { + rc = pWiFiEventHandler->staGotIp(event->event_info.got_ip); + break; + } + + case SYSTEM_EVENT_STA_START: { + rc = pWiFiEventHandler->staStart(); + break; + } + + case SYSTEM_EVENT_STA_STOP: { + rc = pWiFiEventHandler->staStop(); + break; + } + + case SYSTEM_EVENT_WIFI_READY: { + rc = pWiFiEventHandler->wifiReady(); + break; + } + + default: + break; } - esp_err_t rc = ESP_OK; - switch(event->event_id) { - - case SYSTEM_EVENT_AP_START: - rc = pWiFiEventHandler->apStart(); - break; - case SYSTEM_EVENT_AP_STOP: - rc = pWiFiEventHandler->apStop(); - break; - case SYSTEM_EVENT_STA_CONNECTED: - rc = pWiFiEventHandler->staConnected(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - rc = pWiFiEventHandler->staDisconnected(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - rc = pWiFiEventHandler->staGotIp(event->event_info.got_ip); - break; - case SYSTEM_EVENT_STA_START: - rc = pWiFiEventHandler->staStart(); - break; - case SYSTEM_EVENT_STA_STOP: - rc = pWiFiEventHandler->staStop(); - break; - case SYSTEM_EVENT_WIFI_READY: - rc = pWiFiEventHandler->wifiReady(); - break; - default: - break; - } - if (pWiFiEventHandler->nextHandler != nullptr) { + + if (pWiFiEventHandler->m_nextHandler != nullptr) { printf("Found a next handler\n"); - rc = eventHandler(pWiFiEventHandler->nextHandler, event); + rc = eventHandler(pWiFiEventHandler->m_nextHandler, event); } else { //printf("NOT Found a next handler\n"); } return rc; -} +} // eventHandler + +/** + * @brief Constructor + */ WiFiEventHandler::WiFiEventHandler() { -} + m_nextHandler = nullptr; +} // WiFiEventHandler /** @@ -79,7 +99,9 @@ WiFiEventHandler::WiFiEventHandler() { * @return The event handler function. */ system_event_cb_t WiFiEventHandler::getEventHandler() { - return eventHandler; + ESP_LOGD(LOG_TAG, ">> getEventHandler()"); + ESP_LOGD(LOG_TAG, "<< getEventHandler: 0x%x", (uint32_t)eventHandler); + return eventHandler; } // getEventHandler @@ -90,65 +112,74 @@ system_event_cb_t WiFiEventHandler::getEventHandler() { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { - ESP_LOGD(tag, "default staGotIp"); + ESP_LOGD(LOG_TAG, "default staGotIp"); return ESP_OK; } // staGotIp + /** * @brief Handle the Access Point started event. * Handle an indication that the ESP32 has started being an access point. * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStart() { - ESP_LOGD(tag, "default apStart"); + ESP_LOGD(LOG_TAG, "default apStart"); return ESP_OK; } // apStart + /** * @brief Handle the Access Point stop event. * Handle an indication that the ESP32 has stopped being an access point. * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStop() { - ESP_LOGD(tag, "default apStop"); + ESP_LOGD(LOG_TAG, "default apStop"); return ESP_OK; } // apStop + esp_err_t WiFiEventHandler::wifiReady() { - ESP_LOGD(tag, "default wifiReady"); + ESP_LOGD(LOG_TAG, "default wifiReady"); return ESP_OK; } // wifiReady + esp_err_t WiFiEventHandler::staStart() { - ESP_LOGD(tag, "default staStart"); + ESP_LOGD(LOG_TAG, "default staStart"); return ESP_OK; } // staStart + esp_err_t WiFiEventHandler::staStop() { - ESP_LOGD(tag, "default staStop"); + ESP_LOGD(LOG_TAG, "default staStop"); return ESP_OK; } // staStop + esp_err_t WiFiEventHandler::staConnected() { - ESP_LOGD(tag, "default staConnected"); + ESP_LOGD(LOG_TAG, "default staConnected"); return ESP_OK; } // staConnected + esp_err_t WiFiEventHandler::staDisconnected() { - ESP_LOGD(tag, "default staDisconnected"); + ESP_LOGD(LOG_TAG, "default staDisconnected"); return ESP_OK; } // staDisconnected + esp_err_t WiFiEventHandler::apStaConnected() { - ESP_LOGD(tag, "default apStaConnected"); + ESP_LOGD(LOG_TAG, "default apStaConnected"); return ESP_OK; } // apStaConnected + esp_err_t WiFiEventHandler::apStaDisconnected() { - ESP_LOGD(tag, "default apStaDisconnected"); + ESP_LOGD(LOG_TAG, "default apStaDisconnected"); return ESP_OK; } // apStaDisconnected + WiFiEventHandler::~WiFiEventHandler() { - delete nextHandler; } // ~WiFiEventHandler diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index 0596e92c..fe39e332 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -98,7 +98,7 @@ class WiFiEventHandler { * @return The next WiFi event handler in the chain or nullptr if there is none. */ WiFiEventHandler *getNextHandler() { - return nextHandler; + return m_nextHandler; } /** @@ -106,12 +106,12 @@ class WiFiEventHandler { * @param [in] nextHandler The next WiFi event handler in the chain. */ void setNextHandler(WiFiEventHandler* nextHandler) { - this->nextHandler = nextHandler; + this->m_nextHandler = nextHandler; } private: - WiFiEventHandler *nextHandler = nullptr; - static esp_err_t eventHandler(void *ctx, system_event_t *event); + WiFiEventHandler *m_nextHandler; + static esp_err_t eventHandler(void* ctx, system_event_t* event); }; #endif /* MAIN_WIFIEVENTHANDLER_H_ */ From 5b074fbe93daac2bff459f4caad853f52b2aa633 Mon Sep 17 00:00:00 2001 From: kolban Date: Thu, 28 Sep 2017 08:45:35 -0500 Subject: [PATCH 005/310] Fixes for issue #93 --- cpp_utils/BLEScan.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index c5a41fc1..54fcac01 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -203,13 +203,10 @@ BLEScanResults BLEScan::start(uint32_t duration) { ESP_LOGD(LOG_TAG, ">> start(%d)", duration); m_semaphoreScanEnd.take("start"); - ESP_LOGD(LOG_TAG, "A"); - m_scanResults.m_vectorAdvertisedDevices.empty(); - ESP_LOGD(LOG_TAG, "B"); + m_scanResults.m_vectorAdvertisedDevices.clear(); esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); - ESP_LOGD(LOG_TAG, "C"); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); From 2f550acbd476b7a8c504833cbf092efeb39ca6f9 Mon Sep 17 00:00:00 2001 From: kolban Date: Thu, 28 Sep 2017 08:48:54 -0500 Subject: [PATCH 006/310] Changes for #92 --- cpp_utils/BLEUtils.cpp | 271 +++++++++++++++++++++++++++++++++++++++++ cpp_utils/BLEUtils.h | 1 + 2 files changed, 272 insertions(+) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 4c088d66..7a3cdf1a 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -31,6 +31,264 @@ static std::map g_addressMap; static std::map g_connIdMap; */ +typedef struct { + uint32_t assignedNumber; + std::string name; +} member_t; + +static const member_t members_ids[] = { + {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"}, + {0, "" } +}; + + typedef struct { uint32_t assignedNumber; std::string name; @@ -1588,6 +1846,19 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { } // gattStatusToString + +std::string BLEUtils::getMember(uint32_t memberId) { + member_t* p = (member_t *)members_ids; + + while (p->name.length() > 0) { + if (p->assignedNumber == memberId) { + return p->name; + } + p++; + } + return "Unknown"; +} + /** * @brief convert a GAP search event to a string. * @param [in] searchEvt diff --git a/cpp_utils/BLEUtils.h b/cpp_utils/BLEUtils.h index b49d0f8f..96066af9 100644 --- a/cpp_utils/BLEUtils.h +++ b/cpp_utils/BLEUtils.h @@ -38,6 +38,7 @@ class BLEUtils { static void registerByAddress(BLEAddress address, BLEClient* pDevice); static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); + static std::string getMember(uint32_t memberId); static std::string buildPrintData(uint8_t* source, size_t length); static void dumpGattClientEvent( esp_gattc_cb_event_t event, From bf61cd7f40c81cdd950c7784e9b88a6bdbe1d434 Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Fri, 29 Sep 2017 14:25:25 +0300 Subject: [PATCH 007/310] Small annoyance i accidentally created --- cpp_utils/WebServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index 2c975939..64659aa7 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -109,7 +109,7 @@ static std::string mongoose_eventToString(int event) { static void dumpHttpMessage(struct http_message *pHttpMessage) { ESP_LOGD(tag, "HTTP Message"); - ESP_LOGD(tag, "Message: %.*s", (int)pHttpMessage->uri.len, pHttpMessage->message.p); + ESP_LOGD(tag, "Message: %.*s", (int)pHttpMessage->message.len, pHttpMessage->message.p); } /* From a78c8cb8d116a5d81c4bab015439bde82f2d31cd Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Fri, 29 Sep 2017 16:03:19 +0300 Subject: [PATCH 008/310] Add sending files by chunks support. Now if the file is too big, the WebServer will send it in chunks instead. You can specify how big is too big in the definition on top of WebServer.h --- cpp_utils/WebServer.cpp | 73 ++++++++++++++++++++++++++++++++++------- cpp_utils/WebServer.h | 11 +++++-- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index 2c975939..b9e18df1 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -139,6 +139,13 @@ static void mongoose_event_handler_web_server( } ESP_LOGD(tag, "Event: %s [%d]", mongoose_eventToString(event).c_str(), mgConnection->sock); switch (event) { + case MG_EV_SEND: { + struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; + WebServer *pWebServer = pWebServerUserData->pWebServer; + pWebServer->continueConnection(mgConnection); + break; + } + case MG_EV_HTTP_REQUEST: { struct http_message *message = (struct http_message *) eventData; dumpHttpMessage(message); @@ -315,9 +322,7 @@ void WebServer::addPathHandler(const std::string& method, const std::string& pat m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); } // addPathHandler -void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, - void (* handler)(WebServer::HTTPRequest* pHttpRequest, - WebServer::HTTPResponse* pHttpResponse)) { +void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, void (* handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { m_pathHandlers.push_back(PathHandler(std::move(method), pathExpr, handler)); } // addPathHandler @@ -482,6 +487,27 @@ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { sendData((uint8_t*) pData, length); } // sendData +void WebServer::HTTPResponse::sendChunkHead() { + if(m_dataSent) { + ESP_LOGE(tag, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); + } + m_dataSent = true; + mg_send_head(m_nc, m_status, -1, m_headers.c_str()); +} + +void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { + mg_send_http_chunk(m_nc, pData, length); +} // sendChunkHead + +void WebServer::HTTPResponse::closeConnection() { + m_nc->flags |= MG_F_SEND_AND_CLOSE; +} + + +void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { + sendData((uint8_t*) pData, length); +} + /** * @brief Set the headers to be sent in the HTTP response. * @param [in] headers The complete set of headers to send to the caller. @@ -564,15 +590,20 @@ void WebServer::processRequest(struct mg_connection *mgConnection, struct http_m filePath += httpResponse.getRootPath(); filePath.append(message->uri.p, message->uri.len); ESP_LOGD(tag, "Opening file: %s", filePath.c_str()); - FILE *file = fopen(filePath.c_str(), "rb"); + FILE* file = fopen(filePath.c_str(), "rb"); if (file != nullptr) { - fseek(file, 0L, SEEK_END); - size_t length = ftell(file); - fseek(file, 0L, SEEK_SET); - uint8_t *pData = (uint8_t *)malloc(length); - fread(pData, length, 1, file); - fclose(file); - httpResponse.sendData(pData, length); + auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); + size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); + + if(read >= MAX_CHUNK_LENGTH) { + httpResponse.sendChunkHead(); + httpResponse.sendChunk((char*)pData, read); + fclose(unfinishedConnection[mgConnection->sock]); + unfinishedConnection[mgConnection->sock] = file; + } else { + fclose(file); + httpResponse.sendData(pData, read); + } free(pData); } else { // Handle unable to open file @@ -581,6 +612,26 @@ void WebServer::processRequest(struct mg_connection *mgConnection, struct http_m } } // processRequest +void WebServer::continueConnection(struct mg_connection* mgConnection) { + if(unfinishedConnection.count(mgConnection->sock) == 0) { + return; + } + + HTTPResponse httpResponse = HTTPResponse(mgConnection); + + FILE* file = unfinishedConnection[mgConnection->sock]; + auto pData = (char*) malloc(MAX_CHUNK_LENGTH); + size_t length = fread(pData, MAX_CHUNK_LENGTH, 1, file); + + httpResponse.sendChunk(pData, length); + if(length < MAX_CHUNK_LENGTH) { + fclose(file); + httpResponse.closeConnection(); + unfinishedConnection.erase(mgConnection->sock); + } + free(pData); +} + /** * @brief Construct an instance of a PathHandler. diff --git a/cpp_utils/WebServer.h b/cpp_utils/WebServer.h index 7bb1e585..ef879f91 100644 --- a/cpp_utils/WebServer.h +++ b/cpp_utils/WebServer.h @@ -12,9 +12,12 @@ #include #include #include "sdkconfig.h" + #ifdef CONFIG_MONGOOSE_PRESENT #include +#define MAX_CHUNK_LENGTH 4090 // 4 kilobytes + class WebServer; /** @@ -59,6 +62,9 @@ class WebServer { const std::string& getRootPath() const; void setRootPath(const std::string& path); void setRootPath(std::string&& path); + void sendChunkHead(); + void sendChunk(const char* pData, size_t length); + void closeConnection(); private: struct mg_connection *m_nc; std::string m_rootPath; @@ -90,8 +96,7 @@ class WebServer { */ class HTTPMultiPart { public: - virtual ~HTTPMultiPart() { - }; + virtual ~HTTPMultiPart() = default; virtual void begin(const std::string& varName, const std::string& fileName); virtual void end(); virtual void data(const std::string& data); @@ -191,11 +196,13 @@ class WebServer { void setWebSocketHandlerFactory(WebSocketHandlerFactory *pWebSocketHandlerFactory); void start(unsigned short port = 80); void processRequest(struct mg_connection *mgConnection, struct http_message *message); + void continueConnection(struct mg_connection* mgConnection); HTTPMultiPartFactory *m_pMultiPartFactory; WebSocketHandlerFactory *m_pWebSocketHandlerFactory; private: std::string m_rootPath; std::vector m_pathHandlers; + std::map unfinishedConnection; }; #endif // CONFIG_MONGOOSE_PRESENT From 90876140650d4f18847cb97e27d7dbb0f8d77bf4 Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Fri, 29 Sep 2017 17:38:52 +0300 Subject: [PATCH 009/310] Fix to the chunk sending system --- cpp_utils/WebServer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index 3758d73d..ebdd54aa 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -621,13 +621,14 @@ void WebServer::continueConnection(struct mg_connection* mgConnection) { FILE* file = unfinishedConnection[mgConnection->sock]; auto pData = (char*) malloc(MAX_CHUNK_LENGTH); - size_t length = fread(pData, MAX_CHUNK_LENGTH, 1, file); + size_t length = fread(pData, 1, MAX_CHUNK_LENGTH, file); httpResponse.sendChunk(pData, length); if(length < MAX_CHUNK_LENGTH) { fclose(file); httpResponse.closeConnection(); unfinishedConnection.erase(mgConnection->sock); + httpResponse.sendChunk("", 0); } free(pData); } From 7d97376a2865b841947d1bdbfdf9e1931db6ad3d Mon Sep 17 00:00:00 2001 From: kolban Date: Fri, 29 Sep 2017 10:24:40 -0500 Subject: [PATCH 010/310] Code changes for #103 --- cpp_utils/BLEAdvertisedDevice.cpp | 4 +- cpp_utils/BLEAdvertisedDevice.h | 2 +- cpp_utils/BLECharacteristicMap.cpp | 122 +++++++++++++------------- cpp_utils/BLEDevice.cpp | 99 +++++++++++---------- cpp_utils/BLEDevice.h | 27 +++--- cpp_utils/BLERemoteCharacteristic.cpp | 78 +++++++++++----- cpp_utils/BLERemoteCharacteristic.h | 6 ++ cpp_utils/BLEScan.cpp | 42 ++------- cpp_utils/BLEScan.h | 17 ++-- cpp_utils/BLEServer.cpp | 11 ++- cpp_utils/BLEServerCallbacks.cpp | 22 ----- cpp_utils/BLEService.cpp | 18 +--- cpp_utils/BLEUUID.h | 2 +- 13 files changed, 230 insertions(+), 220 deletions(-) delete mode 100644 cpp_utils/BLEServerCallbacks.cpp diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index f1752eda..5074b811 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -60,7 +60,7 @@ BLEAddress BLEAdvertisedDevice::getAddress() { * * @return The appearance of the advertised device. */ -uint16_t BLEAdvertisedDevice::getApperance() { +uint16_t BLEAdvertisedDevice::getAppearance() { return m_appearance; } @@ -390,7 +390,7 @@ std::string BLEAdvertisedDevice::toString() { std::stringstream ss; ss << "Name: " << getName() << ", Address: " << getAddress().toString(); if (haveAppearance()) { - ss << ", appearance: " << getApperance(); + ss << ", appearance: " << getAppearance(); } if (haveManufacturerData()) { char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index 2fb26522..fbdeeec7 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -30,7 +30,7 @@ class BLEAdvertisedDevice { BLEAdvertisedDevice(); BLEAddress getAddress(); - uint16_t getApperance(); + uint16_t getAppearance(); std::string getManufacturerData(); std::string getName(); int getRSSI(); diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index f475e835..6ded0a63 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -10,6 +10,17 @@ #include #include "BLEService.h" + +/** + * @brief Return the characteristic by handle. + * @param [in] handle The handle to look up the characteristic. + * @return The characteristic. + */ +BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + /** * @brief Return the characteristic by UUID. * @param [in] UUID The UUID to look up the characteristic. @@ -19,6 +30,7 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) { return getByUUID(BLEUUID(uuid)); } + /** * @brief Return the characteristic by UUID. * @param [in] UUID The UUID to look up the characteristic. @@ -36,26 +48,49 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { /** - * @brief Return the characteristic by handle. - * @param [in] handle The handle to look up the characteristic. - * @return The characteristic. + * @brief Get the first characteristic in the map. + * @return The first characteristic in the map. */ -BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { - return m_handleMap.at(handle); -} // getByHandle +BLECharacteristic* BLECharacteristicMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLECharacteristic* pRet = m_iterator->second; + m_iterator++; + return pRet; +} // getFirst /** - * @brief Set the characteristic by UUID. - * @param [in] uuid The uuid of the characteristic. - * @param [in] characteristic The characteristic to cache. - * @return N/A. + * @brief Get the next characteristic in the map. + * @return The next characteristic in the map. */ -void BLECharacteristicMap::setByUUID( - BLEUUID uuid, - BLECharacteristic *pCharacteristic) { - m_uuidMap.insert(std::pair(uuid.toString(), pCharacteristic)); -} // setByUUID +BLECharacteristic* BLECharacteristicMap::getNext() { + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLECharacteristic* pRet = m_iterator->second; + m_iterator++; + return pRet; +} // getNext + + +/** + * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +void BLECharacteristicMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.second->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent /** @@ -70,6 +105,19 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, } // setByHandle +/** + * @brief Set the characteristic by UUID. + * @param [in] uuid The uuid of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void BLECharacteristicMap::setByUUID( + BLEUUID uuid, + BLECharacteristic *pCharacteristic) { + m_uuidMap.insert(std::pair(uuid.toString(), pCharacteristic)); +} // setByUUID + + /** * @brief Return a string representation of the characteristic map. * @return A string representation of the characteristic map. @@ -89,48 +137,4 @@ std::string BLECharacteristicMap::toString() { } // toString -/** - * @breif Pass the GATT server event onwards to each of the characteristics found in the mapping - * @param [in] event - * @param [in] gatts_if - * @param [in] param - */ -void BLECharacteristicMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); - } -} // handleGATTServerEvent - - -/** - * @brief Get the first characteristic in the map. - * @return The first characteristic in the map. - */ -BLECharacteristic* BLECharacteristicMap::getFirst() { - m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLECharacteristic *pRet = m_iterator->second; - m_iterator++; - return pRet; -} // getFirst - - -/** - * @brief Get the next characteristic in the map. - * @return The next characteristic in the map. - */ -BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLECharacteristic *pRet = m_iterator->second; - m_iterator++; - return pRet; -} // getNext #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index f383f371..4470a48a 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -7,21 +7,20 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include +#include #include #include #include -#include -#include -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -// ESP32 BLE -#include // ESP32 BLE -#include // ESP32 ESP-IDF -#include // ESP32 ESP-IDF -#include // Part of C++ STL -#include -#include +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 ESP-IDF +#include // ESP32 ESP-IDF +#include // Part of C++ Standard library +#include // Part of C++ Standard library +#include // Part of C++ Standard library #include "BLEDevice.h" #include "BLEClient.h" @@ -30,41 +29,54 @@ static const char* LOG_TAG = "BLEDevice"; -BLEServer *BLEDevice::m_bleServer = nullptr; -BLEScan *BLEDevice::m_pScan = nullptr; -BLEClient *BLEDevice::m_pClient = nullptr; - -#include +/** + * Singletons for the BLEDevice. + */ +BLEServer* BLEDevice::m_pServer = nullptr; +BLEScan* BLEDevice::m_pScan = nullptr; +BLEClient* BLEDevice::m_pClient = nullptr; +/** + * @brief Create a new instance of a client. + * @return A new instance of the client. + */ BLEClient* BLEDevice::createClient() { m_pClient = new BLEClient(); return m_pClient; } // createClient + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ BLEServer* BLEDevice::createServer() { - return new BLEServer(); -} + m_pServer = new BLEServer(); + return m_pServer; +} // createServer /** * @brief Handle GATT server events. * - * @param [in] event - * @param [in] gatts_if - * @param [in] param + * @param [in] event The event that has been newly received. + * @param [in] gatts_if The connection to the GATT interface. + * @param [in] param Parameters for the event. */ void BLEDevice::gattServerEventHandler( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param + esp_ble_gatts_cb_param_t* param ) { ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", gatts_if, BLEUtils::gattServerEventTypeToString(event).c_str()); + BLEUtils::dumpGattServerEvent(event, gatts_if, param); - if (BLEDevice::m_bleServer != nullptr) { - BLEDevice::m_bleServer->handleGATTServerEvent(event, gatts_if, param); + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); } } // gattServerEventHandler @@ -73,10 +85,6 @@ void BLEDevice::gattServerEventHandler( * @brief Handle GATT client events. * * Handler for the GATT client events. - * * `ESP_GATTC_OPEN_EVT` – Invoked when a connection is opened. - * * `ESP_GATTC_PREP_WRITE_EVT` – Response to write a characteristic. - * * `ESP_GATTC_READ_CHAR_EVT` – Response to read a characteristic. - * * `ESP_GATTC_REG_EVT` – Invoked when a GATT client has been registered. * * @param [in] event * @param [in] gattc_if @@ -90,12 +98,13 @@ void BLEDevice::gattClientEventHandler( ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); BLEUtils::dumpGattClientEvent(event, gattc_if, param); - +/* switch(event) { default: { break; } } // switch + */ // If we have a client registered, call it. if (BLEDevice::m_pClient != nullptr) { @@ -128,8 +137,8 @@ void BLEDevice::gapEventHandler( } } // switch - if (BLEDevice::m_bleServer != nullptr) { - BLEDevice::m_bleServer->handleGAPEvent(event, param); + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGAPEvent(event, param); } if (BLEDevice::m_pScan != nullptr) { @@ -138,6 +147,18 @@ void BLEDevice::gapEventHandler( } // gapEventHandler +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. + */ +BLEScan* BLEDevice::getScan() { + if (m_pScan == nullptr) { + m_pScan = new BLEScan(); + } + return m_pScan; +} // getScan + + /** * @brief Initialize the %BLE environment. * @param deviceName The device name of the device. @@ -209,18 +230,4 @@ void BLEDevice::init(std::string deviceName) { } // init - -/** - * @brief Retrieve the Scan object that we use for scanning. - * @return The scanning object reference. - */ -BLEScan* BLEDevice::getScan() { - if (m_pScan == nullptr) { - m_pScan = new BLEScan(); - } - return m_pScan; -} // getScan - - - #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 28d78cb4..eb9fdaa7 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -24,31 +24,34 @@ */ class BLEDevice { public: - static void dumpDevices(); + static BLEClient* createClient(); static BLEServer* createServer(); + static void dumpDevices(); + static BLEScan* getScan(); + static void init(std::string deviceName); - static void init(std::string deviceName); - //static void scan(int duration, esp_ble_scan_type_t scan_type = BLE_SCAN_TYPE_PASSIVE); - static BLEScan *getScan(); - static BLEServer *m_bleServer; +private: + static BLEServer *m_pServer; static BLEScan *m_pScan; static BLEClient *m_pClient; -private: static esp_gatt_if_t getGattcIF(); static void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param); + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param); + static void gattServerEventHandler( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param); + esp_ble_gatts_cb_param_t* param); + static void gapEventHandler( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param); + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + }; // class BLE #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index fdd5b1ff..81e6725e 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -54,6 +54,60 @@ BLERemoteCharacteristic::~BLERemoteCharacteristic() { } // ~BLERemoteCharacteristic +/** + * @brief Does the characteristic support broadcasting? + * @return True if the characteristic supports broadcasting. + */ +bool BLERemoteCharacteristic::canBroadcast() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0; +} // canBroadcast + + +/** + * @brief Does the characteristic support indications? + * @return True if the characteristic supports indications. + */ +bool BLERemoteCharacteristic::canIndicate() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0; +} // canIndicate + + +/** + * @brief Does the characteristic support notifications? + * @return True if the characteristic supports notifications. + */ +bool BLERemoteCharacteristic::canNotify() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0; +} // canNotify + + +/** + * @brief Does the characteristic support reading? + * @return True if the characteristic supports reading. + */ +bool BLERemoteCharacteristic::canRead() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0; +} // canRead + + +/** + * @brief Does the characteristic support writing? + * @return True if the characteristic supports writing. + */ +bool BLERemoteCharacteristic::canWrite() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0; +} // canWrite + + +/** + * @brief Does the characteristic support writing with no response? + * @return True if the characteristic supports writing with no response. + */ +bool BLERemoteCharacteristic::canWriteNoResponse() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0; +} // canWriteNoResponse + + /* static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { if (id1.id.inst_id != id2.id.inst_id) { @@ -209,25 +263,6 @@ void BLERemoteCharacteristic::getDescriptors() { removeDescriptors(); // Remove any existing descriptors. - /* - uint16_t count; - esp_gatt_status_t status = ::esp_ble_gattc_get_attr_count( - getRemoteService()->getClient()->getGattcIf(), - getRemoteService()->getClient()->getConnId(), - ESP_GATT_DB_DESCRIPTOR, - getRemoteService()->getStartHandle(), - getRemoteService()->getEndHandle(), - getHandle(), // Characteristic handle ... only used for ESP_GATT_DB_DESCRIPTOR - &count - ); - if (status != ESP_GATT_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_attr_count: %s", BLEUtils::gattStatusToString(status).c_str()); - } else { - ESP_LOGD(LOG_TAG, "Number of descriptors associated with characteristic is %d", count); - } - */ - - // Loop over each of the descriptors within the service associated with this characteristic. // For each descriptor we find, create a BLERemoteDescriptor instance. uint16_t offset = 0; @@ -308,7 +343,7 @@ BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { * @brief Get the remote service associated with this characteristic. * @return The remote service associated with this characteristic. */ -BLERemoteService *BLERemoteCharacteristic::getRemoteService() { +BLERemoteService* BLERemoteCharacteristic::getRemoteService() { return m_pRemoteService; } // getRemoteService @@ -371,6 +406,8 @@ std::string BLERemoteCharacteristic::readValue() { m_semaphoreReadCharEvt.take("readValue"); // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + // This is an asynchronous request which means that we must block waiting for the response + // to become available. esp_err_t errRc = ::esp_ble_gattc_read_char( m_pRemoteService->getClient()->getGattcIf(), m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server @@ -524,5 +561,4 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp writeValue(std::string((char *)data, length), response); } // writeValue - #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 5af55a69..b34e3649 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -30,6 +30,12 @@ class BLERemoteCharacteristic { ~BLERemoteCharacteristic(); // Public member functions + bool canBroadcast(); + bool canIndicate(); + bool canNotify(); + bool canRead(); + bool canWrite(); + bool canWriteNoResponse(); BLERemoteDescriptor *getDescriptor(BLEUUID uuid); BLEUUID getUUID(); std::string readValue(void); diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 54fcac01..925c09db 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -21,32 +21,20 @@ static const char* LOG_TAG = "BLEScan"; +/** + * Constructor + */ BLEScan::BLEScan() { m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; setInterval(100); setWindow(100); - m_pAdvertisedDeviceCallbacks = nullptr; - m_stopped = true; } // BLEScan - -/** - * @brief Clear the history of previously detected advertised devices. - * @return N/A - */ -/* -void BLEScan::clearAdvertisedDevices() { - for (int i=0; iscan_rst.bda); bool found = false; - /* - for (int i=0; igetAddress().equals(advertisedAddress)) { - found = true; - break; - } - } - */ + for (int i=0; iscan_rst.ble_adv); advertisedDevice.setScan(this); - //m_vectorAvdertisedDevices.push_back(pAdvertisedDevice); if (m_pAdvertisedDeviceCallbacks) { m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); } @@ -144,14 +124,6 @@ void BLEScan::gapEventHandler( } // gapEventHandler -/* -void BLEScan::onResults() { - ESP_LOGD(LOG_TAG, ">> onResults: default"); - ESP_LOGD(LOG_TAG, "<< onResults"); -} // onResults -*/ - - /** * @brief Should we perform an active or passive scan? * The default is a passive scan. An active scan means that we will wish a scan response. @@ -200,7 +172,7 @@ void BLEScan::setWindow(uint16_t windowMSecs) { * @return N/A. */ BLEScanResults BLEScan::start(uint32_t duration) { - ESP_LOGD(LOG_TAG, ">> start(%d)", duration); + ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); m_semaphoreScanEnd.take("start"); diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index f9575eac..bc7f4314 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -21,10 +21,19 @@ class BLEAdvertisedDeviceCallbacks; class BLEClient; class BLEScan; + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ class BLEScanResults { public: - int getCount(); + int getCount(); BLEAdvertisedDevice getDevice(uint32_t i); + private: friend BLEScan; std::vector m_vectorAdvertisedDevices; @@ -39,7 +48,6 @@ class BLEScan { public: BLEScan(); - //virtual void onResults(); void setActiveScan(bool active); void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks); void setInterval(uint16_t intervalMSecs); @@ -58,9 +66,8 @@ class BLEScan { esp_ble_scan_params_t m_scan_params; BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; bool m_stopped; - FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); - //std::vector m_vectorAvdertisedDevices; - BLEScanResults m_scanResults; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + BLEScanResults m_scanResults; }; // BLEScan #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index be9773d9..57d70d2d 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -35,7 +35,6 @@ BLEServer::BLEServer() { m_gatts_if = -1; m_connectedCount = 0; m_connId = -1; - BLEDevice::m_bleServer = this; m_pServerCallbacks = nullptr; createApp(0); @@ -350,4 +349,14 @@ void BLEServer::addCharacteristic(BLECharacteristic *characteristic, BLEService } */ +void BLEServerCallbacks::onConnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + +void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); +} // onDisconnect + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEServerCallbacks.cpp b/cpp_utils/BLEServerCallbacks.cpp deleted file mode 100644 index 88087209..00000000 --- a/cpp_utils/BLEServerCallbacks.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * BLEServerCallbacks.cpp - * - * Created on: Jul 4, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include "BLEServer.h" -#include -static const char* LOG_TAG = "BLEServerCallbacks"; - -void BLEServerCallbacks::onConnect(BLEServer* pServer) { - ESP_LOGD(LOG_TAG, ">> onConnect(): Default"); - ESP_LOGD(LOG_TAG, "<< onConnect()"); -} - -void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { - ESP_LOGD(LOG_TAG, ">> onDisconnect(): Default"); - ESP_LOGD(LOG_TAG, "<< onDisconnect()"); -} -#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index ec16db88..4810f07b 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -57,12 +57,11 @@ void BLEService::executeCreate(BLEServer *pServer) { ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); m_pServer = pServer; + m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + esp_gatt_srvc_id_t srvc_id; srvc_id.id.inst_id = 0; srvc_id.id.uuid = *m_uuid.getNative(); - - m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT - esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, 10); if (errRc != ESP_OK) { @@ -87,18 +86,6 @@ void BLEService::dump() { ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); } // dump -/* -void BLEService::setService(esp_gatt_srvc_id_t srvc_id) { - m_srvc_id = srvc_id; -} -*/ - -/* -esp_gatt_srvc_id_t BLEService::getService() { - return m_srvc_id; -} -*/ - /** * @brief Get the UUID of the service. @@ -212,6 +199,7 @@ BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t p return createCharacteristic(BLEUUID(uuid), properties); } + /** * @brief Create a new BLE Characteristic associated with this service. * @param [in] uuid - The UUID of the characteristic. diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index da0e594c..cfce7c93 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -31,7 +31,7 @@ class BLEUUID { private: esp_bt_uuid_t m_uuid; - bool m_valueSet; + bool m_valueSet; // Is there a value set for this instance. }; // BLEUUID #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ From cb84cd9a380d8b8b500ee6920d16008fee9b1e31 Mon Sep 17 00:00:00 2001 From: kolban Date: Fri, 29 Sep 2017 22:03:01 -0500 Subject: [PATCH 011/310] Work for #98 --- cpp_utils/BLEClient.cpp | 2 +- cpp_utils/BLERemoteService.cpp | 13 ++++++++-- cpp_utils/BLERemoteService.h | 3 ++- cpp_utils/WiFi.cpp | 43 ++++++++++++++++++++++++++-------- cpp_utils/WiFi.h | 12 +++++----- cpp_utils/WiFiEventHandler.h | 1 + 6 files changed, 54 insertions(+), 20 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index d518d531..645c4aea 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -281,7 +281,7 @@ std::map* BLEClient::getServices() { * and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received. */ ESP_LOGD(LOG_TAG, ">> getServices"); - m_servicesMap.empty(); + m_servicesMap.clear(); esp_err_t errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index b9fd29de..712954d4 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -140,7 +140,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { // asked the device about its characteristics, then we do that now. Once we get the results we can then // examine the characteristics map to see if it has the characteristic we are looking for. if (!m_haveCharacteristics) { - getCharacteristics(); + retrieveCharacteristics(); } std::string v = uuid.toString(); for (auto &myPair : m_characteristicMap) { @@ -156,7 +156,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { * @brief Retrieve all the characteristics for this service. * @return N/A */ -void BLERemoteService::getCharacteristics() { +void BLERemoteService::retrieveCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); @@ -262,6 +262,15 @@ void BLERemoteService::getCharacteristics() { } // getCharacteristics +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* BLERemoteService::getCharacteristics() { + return &m_characteristicMap; +} // getCharacteristics + + BLEClient* BLERemoteService::getClient() { return m_pClient; } diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index a6241cc0..75262868 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -32,6 +32,7 @@ class BLERemoteService { // Public methods BLERemoteCharacteristic* getCharacteristic(const char* uuid); BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); + std::map* getCharacteristics(); BLEClient* getClient(void); BLEUUID getUUID(void); @@ -46,7 +47,7 @@ class BLERemoteService { friend class BLERemoteCharacteristic; // Private methods - void getCharacteristics(void); + void retrieveCharacteristics(void); uint16_t getHandle(); esp_gatt_id_t* getSrvcId(void); uint16_t getStartHandle(); diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index cc556ec1..ecbb76e0 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -41,6 +41,9 @@ static void setDNSServer(char *ip) { } */ + + + /** * @brief Creates and uses a default event handler */ @@ -48,20 +51,20 @@ WiFi::WiFi() : ip(0) , gw(0) , netmask(0) - , m_wifiEventHandler(nullptr) + , m_pWifiEventHandler(nullptr) { ::nvs_flash_init(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&config); ::tcpip_adapter_init(); - m_wifiEventHandler = new WiFiEventHandler(); + m_pWifiEventHandler = new WiFiEventHandler(); } // WiFi /** * @brief Deletes the event handler that was used by the class */ WiFi::~WiFi() { - delete m_wifiEventHandler; + delete m_pWifiEventHandler; } /** @@ -134,11 +137,12 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * * The event handler will be called back with the outcome of the connection. * - * @param[in] ssid The network SSID of the access point to which we wish to connect. - * @param[in] password The password of the access point to which we wish to connect. + * @param [in] ssid The network SSID of the access point to which we wish to connect. + * @param [in] password The password of the access point to which we wish to connect. + * @param [in] waitForConnection Block until the connection has an outcome. * @return N/A. */ -void WiFi::connectAP(const std::string& ssid, const std::string& password){ +void WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ ESP_LOGD(LOG_TAG, ">> connectAP"); if (ip != 0 && gw != 0 && netmask != 0) { @@ -153,7 +157,8 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password){ } - ESP_ERROR_CHECK( esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); + ESP_ERROR_CHECK(esp_event_loop_init(WiFi::eventHandler, this)); + //ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); wifi_config_t sta_config; @@ -164,7 +169,9 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password){ ESP_ERROR_CHECK(::esp_wifi_set_config(WIFI_IF_STA, &sta_config)); ESP_ERROR_CHECK(::esp_wifi_start()); + m_gotIpEvt.take("connectAP"); ESP_ERROR_CHECK(::esp_wifi_connect()); + m_gotIpEvt.wait("connectAP"); ESP_LOGD(LOG_TAG, "<< connectAP"); } // connectAP @@ -180,6 +187,22 @@ void WiFi::dump() { ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); } // dump + +/** + * @brief Primary event handler interface. + */ +esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { + WiFi *pWiFi = (WiFi *)ctx; + esp_err_t rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + // If the event we received indicates that we now have an IP address then unlock the mutex that + // indicates we are waiting for an IP. + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { + pWiFi->m_gotIpEvt.give(); + } + return rc; +} // eventHandler + + /** * @brief Get the AP IP Info. * @return The AP IP Info. @@ -311,7 +334,7 @@ std::string WiFi::getStaSSID() { std::vector WiFi::scan() { ::nvs_flash_init(); ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); + ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); @@ -353,7 +376,7 @@ std::vector WiFi::scan() { void WiFi::startAP(const std::string& ssid, const std::string& password) { ::nvs_flash_init(); ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(m_wifiEventHandler->getEventHandler(), m_wifiEventHandler)); + ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); @@ -378,7 +401,7 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { * @param[in] wifiEventHandler The class that will be used to process events. */ void WiFi::setWifiEventHandler(WiFiEventHandler *wifiEventHandler) { - this->m_wifiEventHandler = wifiEventHandler; + this->m_pWifiEventHandler = wifiEventHandler; } // setWifiEventHandler diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 1299f9f1..3279f284 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -12,6 +12,7 @@ #include #include #include +#include "FreeRTOS.h" #include "WiFiEventHandler.h" /** @@ -103,10 +104,13 @@ class WiFiAPRecord { */ class WiFi { private: + static esp_err_t eventHandler(void* ctx, system_event_t* event); uint32_t ip; uint32_t gw; uint32_t netmask; - WiFiEventHandler *m_wifiEventHandler; + WiFiEventHandler* m_pWifiEventHandler; + uint8_t m_dnsCount=0; + FreeRTOS::Semaphore m_gotIpEvt = FreeRTOS::Semaphore("GotIpEvt"); public: WiFi(); @@ -119,7 +123,7 @@ class WiFi { void setDNSServer(int numdns, ip_addr_t ip); struct in_addr getHostByName(const std::string& hostName); struct in_addr getHostByName(const char* hostName); - void connectAP(const std::string& ssid, const std::string& password); + void connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); void dump(); static std::string getApMac(); static tcpip_adapter_ip_info_t getApIpInfo(); @@ -134,10 +138,6 @@ class WiFi { void setIPInfo(const char* ip, const char* gw, const char* netmask); void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); -private: - uint8_t m_dnsCount=0; - //char *m_dnsServer = nullptr; - }; #endif /* MAIN_WIFI_H_ */ diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index fe39e332..0791089a 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -110,6 +110,7 @@ class WiFiEventHandler { } private: + friend class WiFi; WiFiEventHandler *m_nextHandler; static esp_err_t eventHandler(void* ctx, system_event_t* event); }; From 16e78d2dc8be2551b6963e0b7b41694a745490b1 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 30 Sep 2017 09:30:04 -0500 Subject: [PATCH 012/310] Fixes for #105 --- cpp_utils/BLEDevice.cpp | 3 ++ cpp_utils/BLERemoteCharacteristic.cpp | 40 +++++++++++++++++++-------- cpp_utils/BLEServer.cpp | 6 ++-- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 4470a48a..b2777e58 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -52,7 +52,10 @@ BLEClient* BLEDevice::createClient() { * @return A new instance of the server. */ BLEServer* BLEDevice::createServer() { + ESP_LOGD(LOG_TAG, ">> createServer"); m_pServer = new BLEServer(); + m_pServer->createApp(0); + ESP_LOGD(LOG_TAG, "<< createServer"); return m_pServer; } // createServer diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 81e6725e..68fbaf38 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -151,12 +151,12 @@ void BLERemoteCharacteristic::gattClientEventHandler( // ESP_GATTC_NOTIFY_EVT // // notify - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda - // - uint16_t handle - // - uint16_t value_len - // - uint8_t* value - // - bool is_notify + // - uint16_t conn_id - The connection identifier of the server. + // - esp_bd_addr_t remote_bda - The device address of the BLE server. + // - uint16_t handle - The handle of the characteristic for which the event is being received. + // - uint16_t value_len - The length of the received data. + // - uint8_t* value - The received data. + // - bool is_notify - True if this is a notify, false if it is an indicate. // // We have received a notification event which means that the server wishes us to know about a notification // piece of data. What we must now do is find the characteristic with the associated handle and then @@ -178,6 +178,7 @@ void BLERemoteCharacteristic::gattClientEventHandler( break; } // ESP_GATTC_NOTIFY_EVT + // // ESP_GATTC_READ_CHAR_EVT // This event indicates that the server has responded to the read request. @@ -221,12 +222,29 @@ void BLERemoteCharacteristic::gattClientEventHandler( break; } - // We have process the notify and can unlock the semaphore. + // We have processed the notify registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT + // + // ESP_GATTC_UNREG_FOR_NOTIFY_EVT + // + // unreg_for_notify: + // - esp_gatt_status_t status + // - uint16_t handle + // + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { + if (evtParam->unreg_for_notify.handle != getHandle()) { + break; + } + // We have processed the notify un-registration and can unlock the semaphore. + m_semaphoreRegForNotifyEvt.give(); + break; + } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT: + + // // ESP_GATTC_WRITE_CHAR_EVT // @@ -446,7 +464,7 @@ void BLERemoteCharacteristic::registerForNotify( m_semaphoreRegForNotifyEvt.take("registerForNotify"); - if (notifyCallback != nullptr) { + if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration. esp_err_t errRc = ::esp_ble_gattc_register_for_notify( m_pRemoteService->getClient()->getGattcIf(), *m_pRemoteService->getClient()->getPeerAddress().getNative(), @@ -456,8 +474,8 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - } // Register - else { + } // End Register + else { // If we weren't passed a callback function, then this is an unregistration. esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( m_pRemoteService->getClient()->getGattcIf(), *m_pRemoteService->getClient()->getPeerAddress().getNative(), @@ -467,7 +485,7 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - } // Unregister + } // End Unregister m_semaphoreRegForNotifyEvt.wait("registerForNotify"); diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 57d70d2d..14e44a0d 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -37,14 +37,14 @@ BLEServer::BLEServer() { m_connId = -1; m_pServerCallbacks = nullptr; - createApp(0); + //createApp(0); } // BLEServer void BLEServer::createApp(uint16_t appId) { m_appId = appId; registerApp(); -} +} // createApp /** @@ -198,7 +198,7 @@ void BLEServer::handleGATTServerEvent( case ESP_GATTS_REG_EVT: { m_gatts_if = gatts_if; - m_semaphoreRegisterAppEvt.give(); + m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. break; } // ESP_GATTS_REG_EVT From 658f0d48f44ebc1c716f73559458772bb45b0101 Mon Sep 17 00:00:00 2001 From: dadosch Date: Mon, 2 Oct 2017 20:10:07 +0200 Subject: [PATCH 013/310] Deleted copy of BLEServerCallback because this file doesn't exist any more --- cpp_utils/Makefile.arduino | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index 345b52bf..609f8450 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -35,7 +35,6 @@ BLE_FILES= \ BLERemoteService.h \ BLEScan.cpp \ BLEScan.h \ - BLEServerCallbacks.cpp \ BLEServer.cpp \ BLEServer.h \ BLEService.cpp \ From e85eefa9b43eaf763083c1ff1080f2aa1d3c28a6 Mon Sep 17 00:00:00 2001 From: kolban Date: Mon, 2 Oct 2017 22:08:25 -0500 Subject: [PATCH 014/310] Fixes for BLE Advertising --- cpp_utils/BLEAdvertising.cpp | 42 +++++++--- cpp_utils/BLEAdvertising.h | 1 + cpp_utils/BLEUtils.cpp | 150 ++++++++++++++++++++++++----------- cpp_utils/I2C.cpp | 124 ++++++++++++++++++----------- cpp_utils/I2C.h | 57 +++++-------- 5 files changed, 233 insertions(+), 141 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index ad076ddd..049aa4dc 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -35,6 +35,20 @@ BLEAdvertising::BLEAdvertising() { m_advData.p_service_uuid = nullptr; m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); + m_advDataScanResponse.set_scan_rsp = true; + m_advDataScanResponse.include_name = false; + m_advDataScanResponse.include_txpower = false; + m_advDataScanResponse.min_interval = 0x20; + m_advDataScanResponse.max_interval = 0x40; + m_advDataScanResponse.appearance = 0x00; + m_advDataScanResponse.manufacturer_len = 0; + m_advDataScanResponse.p_manufacturer_data = nullptr; + m_advDataScanResponse.service_data_len = 0; + m_advDataScanResponse.p_service_data = nullptr; + m_advDataScanResponse.service_uuid_len = 0; + m_advDataScanResponse.p_service_uuid = nullptr; + m_advDataScanResponse.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); + m_advParams.adv_int_min = 0x20; m_advParams.adv_int_max = 0x40; m_advParams.adv_type = ADV_TYPE_IND; @@ -83,18 +97,18 @@ void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { esp_bt_uuid_t* espUUID = m_serviceUUID.getNative(); switch(espUUID->len) { case ESP_UUID_LEN_16: { - m_advData.service_uuid_len = 2; - m_advData.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid16); + m_advDataScanResponse.service_uuid_len = 2; + m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid16); break; } case ESP_UUID_LEN_32: { - m_advData.service_uuid_len = 4; - m_advData.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid32); + m_advDataScanResponse.service_uuid_len = 4; + m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid32); break; } case ESP_UUID_LEN_128: { - m_advData.service_uuid_len = 16; - m_advData.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid128); + m_advDataScanResponse.service_uuid_len = 16; + m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid128); break; } } // switch @@ -110,13 +124,13 @@ void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start"); - if (m_advData.service_uuid_len > 0) { + if (m_advDataScanResponse.service_uuid_len > 0) { uint8_t hexData[16*2+1]; - BLEUtils::buildHexData(hexData, m_advData.p_service_uuid, m_advData.service_uuid_len); + BLEUtils::buildHexData(hexData, m_advDataScanResponse.p_service_uuid, m_advDataScanResponse.service_uuid_len); ESP_LOGD(LOG_TAG, " - Service: service_uuid_len=%d, p_service_uuid=0x%x (data=%s)", - m_advData.service_uuid_len, - (uint32_t)m_advData.p_service_uuid, - (m_advData.service_uuid_len > 0?(char *)hexData:"N/A") + m_advDataScanResponse.service_uuid_len, + (uint32_t)m_advDataScanResponse.p_service_uuid, + (m_advDataScanResponse.service_uuid_len > 0?(char *)hexData:"N/A") ); } // We have a service to advertise @@ -128,6 +142,12 @@ void BLEAdvertising::start() { return; } + errRc = ::esp_ble_gap_config_adv_data(&m_advDataScanResponse); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); if (errRc != ESP_OK) { diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 2d0b51c2..afe761d8 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -27,6 +27,7 @@ class BLEAdvertising { void setServiceUUID(BLEUUID serviceUUID); private: esp_ble_adv_data_t m_advData; + esp_ble_adv_data_t m_advDataScanResponse; esp_ble_adv_params_t m_advParams; BLEUUID m_serviceUUID; }; diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 7a3cdf1a..a5fa6239 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -965,18 +965,35 @@ void BLEUtils::dumpGapEvent( switch(event) { // // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT + // adv_data_cmpl + // - esp_bt_status_t // case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { + ESP_LOGD(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); break; } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT @@ -984,8 +1001,11 @@ void BLEUtils::dumpGapEvent( // // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT // + // adv_stop_cmpl + // - esp_bt_status_t status + // case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT @@ -993,6 +1013,15 @@ void BLEUtils::dumpGapEvent( // // 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: { ESP_LOGD(LOG_TAG, "[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(), @@ -1006,6 +1035,18 @@ void BLEUtils::dumpGapEvent( } // 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: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + break; + } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + + // // ESP_GAP_BLE_LOCAL_IR_EVT // @@ -1033,6 +1074,23 @@ void BLEUtils::dumpGapEvent( } // 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: { + ESP_LOGD(LOG_TAG, "[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 // @@ -1090,15 +1148,6 @@ void BLEUtils::dumpGapEvent( } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT - // - // ESP_GAP_BLE_SEC_REQ_EVT - // - case ESP_GAP_BLE_SEC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); - break; - } // ESP_GAP_BLE_SEC_REQ_EVT - - // // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // @@ -1125,6 +1174,14 @@ void BLEUtils::dumpGapEvent( } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT + // + // ESP_GAP_BLE_SEC_REQ_EVT + // + case ESP_GAP_BLE_SEC_REQ_EVT: { + ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + break; + } // ESP_GAP_BLE_SEC_REQ_EVT + default: { ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; @@ -1637,60 +1694,61 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { */ const char* BLEUtils::gapEventToString(uint32_t eventType) { switch(eventType) { - 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_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_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_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_SEC_REQ_EVT: /* BLE security request */ - return "ESP_GAP_BLE_SEC_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_OOB_REQ_EVT: /* OOB request event */ - return "ESP_GAP_BLE_OOB_REQ_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_ADV_STOP_COMPLETE_EVT: /*!< When stop adv complete, the event comes */ - return "ESP_GAP_BLE_ADV_STOP_COMPLETE_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"; - case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: - return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; - case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: - return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; - case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: - return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_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_READ_RSSI_COMPLETE_EVT: - return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; + default: ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 636efd8c..d6a7121a 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -22,11 +22,11 @@ static bool debug = false; * @return N/A. */ I2C::I2C() { - directionKnown = false; - address = 0; - cmd = 0; - sdaPin = DEFAULT_SDA_PIN; - sclPin = DEFAULT_CLK_PIN; + m_directionKnown = false; + m_address = 0; + m_cmd = 0; + m_sdaPin = DEFAULT_SDA_PIN; + m_sclPin = DEFAULT_CLK_PIN; } // I2C @@ -40,9 +40,9 @@ void I2C::beginTransaction() { if (debug) { ESP_LOGD(LOG_TAG, "beginTransaction()"); } - cmd = ::i2c_cmd_link_create(); - ESP_ERROR_CHECK(::i2c_master_start(cmd)); - directionKnown = false; + m_cmd = ::i2c_cmd_link_create(); + ESP_ERROR_CHECK(::i2c_master_start(m_cmd)); + m_directionKnown = false; } // beginTransaction @@ -57,13 +57,24 @@ void I2C::endTransaction() { if (debug) { ESP_LOGD(LOG_TAG, "endTransaction()"); } - ESP_ERROR_CHECK(::i2c_master_stop(cmd)); - ESP_ERROR_CHECK(::i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000/portTICK_PERIOD_MS)); - ::i2c_cmd_link_delete(cmd); - directionKnown = false; + ESP_ERROR_CHECK(::i2c_master_stop(m_cmd)); + ESP_ERROR_CHECK(::i2c_master_cmd_begin(I2C_NUM_0, m_cmd, 1000/portTICK_PERIOD_MS)); + ::i2c_cmd_link_delete(m_cmd); + m_directionKnown = false; } // endTransaction +/** + * @brief Get the address of the %I2C slave against which we are working. + * + * @return The address of the %I2C slave. + */ +uint8_t I2C::getAddress() const +{ + return m_address; +} + + /** * @brief Initialize the I2C interface. * @@ -74,9 +85,9 @@ void I2C::endTransaction() { */ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin) { ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d", address, sdaPin, sclPin); - this->sdaPin = sdaPin; - this->sclPin = sclPin; - this->address = address; + this->m_sdaPin = sdaPin; + this->m_sclPin = sclPin; + this->m_address = address; i2c_config_t conf; conf.mode = I2C_MODE_MASTER; conf.sda_io_num = sdaPin; @@ -104,11 +115,11 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=%d, ack=%d)", length, ack); } - if (directionKnown == false) { - directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, !ack)); + if (m_directionKnown == false) { + m_directionKnown = true; + ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack)); } - ESP_ERROR_CHECK(::i2c_master_read(cmd, bytes, length, !ack)); + ESP_ERROR_CHECK(::i2c_master_read(m_cmd, bytes, length, !ack)); } // read @@ -123,11 +134,11 @@ void I2C::read(uint8_t *byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=1, ack=%d)", ack); } - if (directionKnown == false) { - directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, !ack)); + if (m_directionKnown == false) { + m_directionKnown = true; + ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack)); } - ESP_ERROR_CHECK(::i2c_master_read_byte(cmd, byte, !ack)); + ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, !ack)); } // readByte @@ -139,33 +150,35 @@ void I2C::read(uint8_t *byte, bool ack) { * @return N/A. */ void I2C::scan() { - int i; - esp_err_t espRc; - printf("Data Pin: %d, Clock Pin: %d\n", this->sdaPin, this->sclPin); + uint8_t i; + printf("Data Pin: %d, Clock Pin: %d\n", this->m_sdaPin, this->m_sclPin); printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); printf("00: "); - for (i=3; i< 0x78; i++) { - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (i << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); - i2c_master_stop(cmd); - - espRc = i2c_master_cmd_begin(I2C_NUM_0, cmd, 100/portTICK_PERIOD_MS); + for (i=3; i<0x78; i++) { if (i%16 == 0) { printf("\n%.2x:", i); } - if (espRc == 0) { + if (slavePresent(i)) { printf(" %.2x", i); } else { printf(" --"); } - //ESP_LOGD(tag, "i=%d, rc=%d (0x%x)", i, espRc, espRc); - i2c_cmd_link_delete(cmd); } printf("\n"); } // scan +/** + * @brief Set the address of the %I2C slave against which we will be working. + * + * @param [in] address The address of the %I2C slave. + */ +void I2C::setAddress(uint8_t address) +{ + this->m_address = address; +} // setAddress + + /** * @brief enable or disable debugging. * @param [in] enabled Should debugging be enabled or disabled. @@ -176,6 +189,23 @@ void I2C::setDebug(bool enabled) { } // setDebug +/** + * @brief Determine if the slave is present and responding. + * @param [in] The address of the slave. + * @return True if the slave is present and false otherwise. + */ +bool I2C::slavePresent(uint8_t address) { + i2c_cmd_handle_t cmd = ::i2c_cmd_link_create(); + ::i2c_master_start(cmd); + ::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); + ::i2c_master_stop(cmd); + + esp_err_t espRc = ::i2c_master_cmd_begin(I2C_NUM_0, cmd, 100/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + return espRc == 0; // Return true if the slave is present and false otherwise. +} // slavePresent + + /** * @brief Add an %I2C start request to the command stream. * @return N/A. @@ -184,7 +214,7 @@ void I2C::start() { if (debug) { ESP_LOGD(LOG_TAG, "start()"); } - ESP_ERROR_CHECK(::i2c_master_start(cmd)); + ESP_ERROR_CHECK(::i2c_master_start(m_cmd)); } // start @@ -196,8 +226,8 @@ void I2C::stop() { if (debug) { ESP_LOGD(LOG_TAG, "stop()"); } - ESP_ERROR_CHECK(::i2c_master_stop(cmd)); - directionKnown = false; + ESP_ERROR_CHECK(::i2c_master_stop(m_cmd)); + m_directionKnown = false; } // stop @@ -212,11 +242,11 @@ void I2C::write(uint8_t byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(val=0x%.2x, ack=%d)", byte, ack); } - if (directionKnown == false) { - directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, !ack)); + if (m_directionKnown == false) { + m_directionKnown = true; + ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack)); } - ESP_ERROR_CHECK(::i2c_master_write_byte(cmd, byte, !ack)); + ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, byte, !ack)); } // write @@ -232,11 +262,11 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(length=%d, ack=%d)", length, ack); } - if (directionKnown == false) { - directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, !ack)); + if (m_directionKnown == false) { + m_directionKnown = true; + ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack)); } - ESP_ERROR_CHECK(::i2c_master_write(cmd, bytes, length, !ack)); + ESP_ERROR_CHECK(::i2c_master_write(m_cmd, bytes, length, !ack)); } // write diff --git a/cpp_utils/I2C.h b/cpp_utils/I2C.h index 5142723c..5d38af67 100644 --- a/cpp_utils/I2C.h +++ b/cpp_utils/I2C.h @@ -18,54 +18,37 @@ */ class I2C { private: - uint8_t address; - i2c_cmd_handle_t cmd; - bool directionKnown; - gpio_num_t sdaPin; - gpio_num_t sclPin; + uint8_t m_address; + i2c_cmd_handle_t m_cmd; + bool m_directionKnown; + gpio_num_t m_sdaPin; + gpio_num_t m_sclPin; public: - I2C(); - void beginTransaction(); - void endTransaction(); /** - * @brief Get the address of the %I2C slave against which we are working. - * - * @return The address of the %I2C slave. + * @brief The default SDA pin. */ - uint8_t getAddress() const - { - return address; - } - - void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN); - void read(uint8_t *bytes, size_t length, bool ack=true); - void read(uint8_t *byte, bool ack=true); - + static const gpio_num_t DEFAULT_SDA_PIN = GPIO_NUM_25; /** - * @brief Set the address of the %I2C slave against which we will be working. - * - * @param[in] address The address of the %I2C slave. + * @brief The default Clock pin. */ - void setAddress(uint8_t address) - { - this->address = address; - } + static const gpio_num_t DEFAULT_CLK_PIN = GPIO_NUM_26; - void setDebug(bool enabled); + I2C(); + void beginTransaction(); + void endTransaction(); + uint8_t getAddress() const; + void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN); + void read(uint8_t* bytes, size_t length, bool ack=true); + void read(uint8_t* byte, bool ack=true); void scan(); + void setAddress(uint8_t address); + void setDebug(bool enabled); + bool slavePresent(uint8_t address); void start(); void stop(); void write(uint8_t byte, bool ack=true); - void write(uint8_t *bytes, size_t length, bool ack=true); - /** - * @brief The default SDA pin. - */ - static const gpio_num_t DEFAULT_SDA_PIN = GPIO_NUM_25; - /** - * @brief The default Clock pin. - */ - static const gpio_num_t DEFAULT_CLK_PIN = GPIO_NUM_26; + void write(uint8_t* bytes, size_t length, bool ack=true); }; #endif /* MAIN_I2C_H_ */ From bdfc8491eba04e24558797eb6579d846192f3acf Mon Sep 17 00:00:00 2001 From: kolban Date: Wed, 4 Oct 2017 09:35:21 -0500 Subject: [PATCH 015/310] Improvements for #98 --- cpp_utils/BLERemoteService.cpp | 13 +++++++++++- cpp_utils/BLEUtils.cpp | 39 +++++++++++++++++++++++++++++++--- cpp_utils/tests/test_rest.cpp | 5 +++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 712954d4..b6999cbf 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -154,6 +154,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { /** * @brief Retrieve all the characteristics for this service. + * This function will not return until we have all the characteristics. * @return N/A */ void BLERemoteService::retrieveCharacteristics() { @@ -256,7 +257,8 @@ void BLERemoteService::retrieveCharacteristics() { m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); offset++; // Increment our count of number of descriptors found. - } + } // Loop forever (until we break inside the loop). + m_haveCharacteristics = true; // Remember that we have received the characteristics. ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); } // getCharacteristics @@ -267,6 +269,12 @@ void BLERemoteService::retrieveCharacteristics() { * @return A map of all the characteristics of this service. */ std::map* BLERemoteService::getCharacteristics() { + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } return &m_characteristicMap; } // getCharacteristics @@ -275,14 +283,17 @@ BLEClient* BLERemoteService::getClient() { return m_pClient; } + uint16_t BLERemoteService::getEndHandle() { return m_endHandle; } + esp_gatt_id_t* BLERemoteService::getSrvcId() { return &m_srvcId; } + uint16_t BLERemoteService::getStartHandle() { return m_startHandle; } diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index a5fa6239..00bfd470 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -959,8 +959,8 @@ const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) { * @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) { + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { ESP_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event)); switch(event) { // @@ -1091,14 +1091,19 @@ void BLEUtils::dumpGapEvent( 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: { ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT + // // ESP_GAP_BLE_SCAN_RESULT_EVT // @@ -1112,6 +1117,7 @@ void BLEUtils::dumpGapEvent( // - ble_adv // - flag // - num_resps + // case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { @@ -1139,9 +1145,23 @@ void BLEUtils::dumpGapEvent( } // 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: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_RSP_DATA_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: { ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); break; @@ -1151,6 +1171,9 @@ void BLEUtils::dumpGapEvent( // // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // + // scan_stop_cmpl + // - esp_bt_status_t status + // case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); break; @@ -1158,7 +1181,16 @@ void BLEUtils::dumpGapEvent( // - // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_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: { ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", @@ -1182,6 +1214,7 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_SEC_REQ_EVT + default: { ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; diff --git a/cpp_utils/tests/test_rest.cpp b/cpp_utils/tests/test_rest.cpp index 4ee38f49..604d3adb 100644 --- a/cpp_utils/tests/test_rest.cpp +++ b/cpp_utils/tests/test_rest.cpp @@ -1,5 +1,10 @@ /* * Test the REST API client. + * This application leverages LibCurl. You must make that package available + * as well as "enable it" from "make menuconfig" and C++ Settings -> libCurl present. + * See also: + * * https://github.com/nkolban/esp32-snippets/issues/108 + * */ #include #include From 1a30b01f1f9e248f7870d37a8dc715f1ef236540 Mon Sep 17 00:00:00 2001 From: kolban Date: Thu, 5 Oct 2017 08:20:00 -0500 Subject: [PATCH 016/310] Fixes for #108 --- cpp_utils/RESTClient.cpp | 6 ++++-- cpp_utils/RESTClient.h | 5 +++-- cpp_utils/component.mk | 3 --- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp_utils/RESTClient.cpp b/cpp_utils/RESTClient.cpp index 5cc259c9..20b5bc3d 100644 --- a/cpp_utils/RESTClient.cpp +++ b/cpp_utils/RESTClient.cpp @@ -4,7 +4,9 @@ * Created on: Mar 12, 2017 * Author: kolban */ -#if defined(ESP_HAVE_CURL) + +#include "sdkconfig.h" +#if defined(CONFIG_LIBCURL_PRESENT) #define _GLIBCXX_USE_C99 // Needed for std::string -> to_string inclusion. @@ -163,4 +165,4 @@ std::string RESTTimings::toString() { "\nTotal: " + std::to_string(m_total); return ret; } // toString -#endif // ESP_HAVE_CURL +#endif // CONFIG_LIBCURL_PRESENT diff --git a/cpp_utils/RESTClient.h b/cpp_utils/RESTClient.h index d9c4e04e..02279c40 100644 --- a/cpp_utils/RESTClient.h +++ b/cpp_utils/RESTClient.h @@ -7,7 +7,8 @@ #ifndef MAIN_RESTCLIENT_H_ #define MAIN_RESTCLIENT_H_ -#if defined(ESP_HAVE_CURL) +#include "sdkconfig.h" +#if defined(CONFIG_LIBCURL_PRESENT) #include #include @@ -120,5 +121,5 @@ class RESTClient { static size_t handleData(void *buffer, size_t size, size_t nmemb, void *userp); void prepForCall(); }; -#endif /* ESP_HAVE_CURL */ +#endif /* CONFIG_LIBCURL_PRESENT */ #endif /* MAIN_RESTCLIENT_H_ */ diff --git a/cpp_utils/component.mk b/cpp_utils/component.mk index 6952ed54..7dacf4a8 100644 --- a/cpp_utils/component.mk +++ b/cpp_utils/component.mk @@ -7,9 +7,6 @@ # please read the ESP-IDF documents if you need to do this. COMPONENT_ADD_INCLUDEDIRS=. -## Uncomment the following line if we have an implementation of libcurl available to us. -##CXXFLAGS+=-DESP_HAVE_CURL - ## Uncomment the following line to enable exception handling #CXXFLAGS+=-fexceptions #CXXFLAGS+= -std=c++11 \ No newline at end of file From 96553747ad699b5652915c9e1d39a02171dee424 Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Fri, 6 Oct 2017 11:03:53 +0300 Subject: [PATCH 017/310] If i opened 192.168.4.1 without any path after, the esp would crash This fixes that --- cpp_utils/WebServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index ebdd54aa..ba871243 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -590,7 +590,10 @@ void WebServer::processRequest(struct mg_connection *mgConnection, struct http_m filePath += httpResponse.getRootPath(); filePath.append(message->uri.p, message->uri.len); ESP_LOGD(tag, "Opening file: %s", filePath.c_str()); - FILE* file = fopen(filePath.c_str(), "rb"); + FILE* file = nullptr; + + if(strcmp(filePath.c_str(), "/") != 0) + file = fopen(filePath.c_str(), "rb"); if (file != nullptr) { auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); From c288b0b276f018f26bbf6ff8f1872cba1e5d5df2 Mon Sep 17 00:00:00 2001 From: kolban Date: Fri, 6 Oct 2017 08:58:12 -0500 Subject: [PATCH 018/310] Enhancement for #99 --- cpp_utils/BLERemoteCharacteristic.cpp | 13 ++++++++++--- cpp_utils/BLERemoteCharacteristic.h | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 68fbaf38..25a01cf0 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -41,7 +41,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; - getDescriptors(); // Get the descriptors for this characteristic + retrieveCharacteristics(); // Get the descriptors for this characteristic ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); } // BLERemoteCharacteristic @@ -276,8 +276,8 @@ void BLERemoteCharacteristic::gattClientEventHandler( /** * @brief Populate the descriptors (if any) for this characteristic. */ -void BLERemoteCharacteristic::getDescriptors() { - ESP_LOGD(LOG_TAG, ">> getDescriptors() for characteristic: %s", getUUID().toString().c_str()); +void BLERemoteCharacteristic::retrieveCharacteristics() { + ESP_LOGD(LOG_TAG, ">> retrieveCharacteristics() for characteristic: %s", getUUID().toString().c_str()); removeDescriptors(); // Remove any existing descriptors. @@ -326,6 +326,13 @@ void BLERemoteCharacteristic::getDescriptors() { } // getDescriptors +/** + * @brief Retrieve the map of descriptors keyed by UUID. + */ +std::map* BLERemoteCharacteristic::getDescriptors() { + return &m_descriptorMap; +} // getDescriptors + /** * @brief Get the handle for this characteristic. diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index b34e3649..c06a06b3 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -59,10 +59,11 @@ class BLERemoteCharacteristic { esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - void getDescriptors(); + std::map* getDescriptors(); uint16_t getHandle(); BLERemoteService* getRemoteService(); void removeDescriptors(); + void retrieveCharacteristics(); // Private properties BLEUUID m_uuid; From 036971d925da09a776a4a5dc23ba20341f592d40 Mon Sep 17 00:00:00 2001 From: kolban Date: Fri, 6 Oct 2017 09:56:04 -0500 Subject: [PATCH 019/310] More chnages for #99 --- cpp_utils/BLERemoteCharacteristic.cpp | 8 ++++---- cpp_utils/BLERemoteCharacteristic.h | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 25a01cf0..2e0fbb0e 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -41,7 +41,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; - retrieveCharacteristics(); // Get the descriptors for this characteristic + retrieveDescriptors(); // Get the descriptors for this characteristic ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); } // BLERemoteCharacteristic @@ -276,8 +276,8 @@ void BLERemoteCharacteristic::gattClientEventHandler( /** * @brief Populate the descriptors (if any) for this characteristic. */ -void BLERemoteCharacteristic::retrieveCharacteristics() { - ESP_LOGD(LOG_TAG, ">> retrieveCharacteristics() for characteristic: %s", getUUID().toString().c_str()); +void BLERemoteCharacteristic::retrieveDescriptors() { + ESP_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); removeDescriptors(); // Remove any existing descriptors. @@ -322,7 +322,7 @@ void BLERemoteCharacteristic::retrieveCharacteristics() { offset++; } // while true //m_haveCharacteristics = true; // Remember that we have received the characteristics. - ESP_LOGD(LOG_TAG, "<< getDescriptors(): Found %d descriptors.", offset); + ESP_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", offset); } // getDescriptors diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index c06a06b3..e764fb31 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -37,6 +37,7 @@ class BLERemoteCharacteristic { bool canWrite(); bool canWriteNoResponse(); BLERemoteDescriptor *getDescriptor(BLEUUID uuid); + std::map* getDescriptors(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); @@ -59,11 +60,11 @@ class BLERemoteCharacteristic { esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - std::map* getDescriptors(); + uint16_t getHandle(); BLERemoteService* getRemoteService(); void removeDescriptors(); - void retrieveCharacteristics(); + void retrieveDescriptors(); // Private properties BLEUUID m_uuid; From a8ff24ddf1190501caadabfd55c715bdfe91f074 Mon Sep 17 00:00:00 2001 From: dentellaluca Date: Sat, 7 Oct 2017 10:40:54 +0200 Subject: [PATCH 020/310] Changed variable name (addr) in struct spi_transaction_t The variable name changed from address to addr in esp-idf, see this commit: https://github.com/espressif/esp-idf/commit/ed1d084aeaef1c7216004354bd5b1a08f4a584cf#diff-c3da310493525a7302b298b32f3ac781 --- hardware/displays/U8G2/u8g2_esp32_hal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/displays/U8G2/u8g2_esp32_hal.c b/hardware/displays/U8G2/u8g2_esp32_hal.c index c4ea7875..49f71c4d 100644 --- a/hardware/displays/U8G2/u8g2_esp32_hal.c +++ b/hardware/displays/U8G2/u8g2_esp32_hal.c @@ -75,7 +75,7 @@ uint8_t u8g2_esp32_msg_comms_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void case U8X8_MSG_BYTE_SEND: { spi_transaction_t trans_desc; - trans_desc.address = 0; + trans_desc.addr = 0; trans_desc.command = 0; trans_desc.flags = 0; trans_desc.length = 8 * arg_int; // Number of bits NOT number of bytes. From 2330db843a0286d4aebd6a196abdc867606d70f4 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 09:34:04 -0500 Subject: [PATCH 021/310] Sync commit 2017-10-07 933CT --- cpp_utils/BLECharacteristic.cpp | 11 ++++++----- cpp_utils/BLERemoteCharacteristic.h | 9 +++++---- cpp_utils/BLERemoteService.cpp | 2 ++ cpp_utils/BLEServer.cpp | 2 +- cpp_utils/BLEService.cpp | 7 +++---- cpp_utils/BLEUUID.cpp | 1 + cpp_utils/BLEUUID.h | 4 ++-- cpp_utils/BLEValue.cpp | 23 +++++++++++++++++++++++ cpp_utils/BLEValue.h | 2 ++ 9 files changed, 45 insertions(+), 16 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index cdc2200e..284032e7 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -94,19 +94,20 @@ void BLECharacteristic::executeCreate(BLEService* pService) { m_semaphoreCreateEvt.take("executeCreate"); - std::string strValue = m_value.getValue(); - + /* esp_attr_value_t value; - value.attr_len = strValue.length(); + value.attr_len = m_value.getLength(); value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; - value.attr_value = (uint8_t*)strValue.data(); + value.attr_value = m_value.getData(); + */ esp_err_t errRc = ::esp_ble_gatts_add_char( m_pService->getHandle(), getUUID().getNative(), static_cast(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), getProperties(), - &value, + //&value, + nullptr, &control); // Whether to auto respond or not. if (errRc != ESP_OK) { diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index e764fb31..0b6750a6 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -36,14 +36,14 @@ class BLERemoteCharacteristic { bool canRead(); bool canWrite(); bool canWriteNoResponse(); - BLERemoteDescriptor *getDescriptor(BLEUUID uuid); + BLERemoteDescriptor* getDescriptor(BLEUUID uuid); std::map* getDescriptors(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); uint16_t readUInt16(void); uint32_t readUInt32(void); - void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)); + void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)); void writeValue(uint8_t* data, size_t length, bool response = false); void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); @@ -75,9 +75,10 @@ class BLERemoteCharacteristic { FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; - void (*m_notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify); + void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. - std::map m_descriptorMap; + std::map m_descriptorMap; }; // BLERemoteCharacteristic #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index b6999cbf..c312e946 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -269,12 +269,14 @@ void BLERemoteService::retrieveCharacteristics() { * @return A map of all the characteristics of this service. */ std::map* BLERemoteService::getCharacteristics() { + ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); // If is possible that we have not read the characteristics associated with the service so do that // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking // call and does not return until all the characteristics are available. if (!m_haveCharacteristics) { retrieveCharacteristics(); } + ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str()); return &m_characteristicMap; } // getCharacteristics diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 14e44a0d..a7e61d46 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -168,7 +168,7 @@ void BLEServer::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", - BLEUtils::gattServerEventTypeToString(event).c_str()); + BLEUtils::gattServerEventTypeToString(event).c_str()); // Invoke the handler for every Service we have. m_serviceMap.handleGATTServerEvent(event, gatts_if, param); diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 4810f07b..96624f2e 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -54,8 +54,8 @@ BLEService::BLEService(BLEUUID uuid) { * @return N/A. */ void BLEService::executeCreate(BLEServer *pServer) { - ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - + //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); + getUUID(); // Needed for a weird bug fix m_pServer = pServer; m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT @@ -70,7 +70,6 @@ void BLEService::executeCreate(BLEServer *pServer) { } m_semaphoreCreateEvt.wait("executeCreate"); - ESP_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate @@ -171,7 +170,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). // ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); - ESP_LOGD(LOG_TAG, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s to service: %s", + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), toString().c_str()); diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index e02a9895..0cffe041 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -35,6 +35,7 @@ static const char* LOG_TAG = "BLEUUID"; * @param [in] size The number of bytes to copy */ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { + assert(size > 0); target+=(size-1); // Point target to the last byte of the target data while (size > 0) { *target = *source; diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index cfce7c93..a11220b9 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -30,8 +30,8 @@ class BLEUUID { std::string toString(); private: - esp_bt_uuid_t m_uuid; - bool m_valueSet; // Is there a value set for this instance. + esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet; // Is there a value set for this instance. }; // BLEUUID #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/cpp_utils/BLEValue.cpp b/cpp_utils/BLEValue.cpp index 1989993a..d36d207a 100644 --- a/cpp_utils/BLEValue.cpp +++ b/cpp_utils/BLEValue.cpp @@ -71,6 +71,24 @@ void BLEValue::commit() { } // commit +/** + * @brief Get a pointer to the data. + * @return A pointer to the data. + */ +uint8_t* BLEValue::getData() { + return (uint8_t*)m_value.data(); +} + + +/** + * @brief Get the length of the data in bytes. + * @return The length of the data in bytes. + */ +size_t BLEValue::getLength() { + return m_value.length(); +} // getLength + + /** * @brief Get the read offset. * @return The read offset into the read. @@ -113,4 +131,9 @@ void BLEValue::setValue(std::string value) { void BLEValue::setValue(uint8_t* pData, size_t length) { m_value = std::string((char*)pData, length); } // setValue + + + + + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEValue.h b/cpp_utils/BLEValue.h index a292c6e1..92a7f9a4 100644 --- a/cpp_utils/BLEValue.h +++ b/cpp_utils/BLEValue.h @@ -21,6 +21,8 @@ class BLEValue { void addPart(uint8_t* pData, size_t length); void cancel(); void commit(); + uint8_t* getData(); + size_t getLength(); uint16_t getReadOffset(); std::string getValue(); void setReadOffset(uint16_t readOffset); From f9d7d31fef93a760580dbef2ebef70dc565cc79e Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 10:32:29 -0500 Subject: [PATCH 022/310] Fixes for #108 --- cpp_utils/tests/test_rest.cpp | 6 +++++- curl/README.md | 2 ++ posix/README.md | 8 ++++++++ posix/posix_shims.c | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 curl/README.md create mode 100644 posix/README.md create mode 100644 posix/posix_shims.c diff --git a/cpp_utils/tests/test_rest.cpp b/cpp_utils/tests/test_rest.cpp index 604d3adb..dc168e64 100644 --- a/cpp_utils/tests/test_rest.cpp +++ b/cpp_utils/tests/test_rest.cpp @@ -2,6 +2,10 @@ * Test the REST API client. * This application leverages LibCurl. You must make that package available * as well as "enable it" from "make menuconfig" and C++ Settings -> libCurl present. + * + * You may also have to include "posix_shims.c" in your compilation to provide resolution + * for Posix calls expected by libcurl that aren't present in ESP-IDF. + * * See also: * * https://github.com/nkolban/esp32-snippets/issues/108 * @@ -37,7 +41,7 @@ class CurlTestTask: public Task { RESTTimings *timings = client.getTimings(); - client.setURL("https://httpbin.org/post"); + client.setURL("http://httpbin.org/post"); client.addHeader("Content-Type", "application/json"); client.post("hello world!"); ESP_LOGD(tag, "Result: %s", client.getResponse().c_str()); diff --git a/curl/README.md b/curl/README.md new file mode 100644 index 00000000..49ea404f --- /dev/null +++ b/curl/README.md @@ -0,0 +1,2 @@ +## Missing Posix functions +LibCurl is a moving target and on occassion, is updated to utilize functions that are not part of the ESP-IDF. For example, at the time of writing, the latest version of LibCurl uses the Posix `access(2)` system call that it previously did not. This call is not present in ESP-IDF. To circumvent the problem, a shim file has been provided in `./posix/posix_shims.c` that provides simple implementations for some missing Posix functions. \ No newline at end of file diff --git a/posix/README.md b/posix/README.md new file mode 100644 index 00000000..f534e952 --- /dev/null +++ b/posix/README.md @@ -0,0 +1,8 @@ +# POSIX +Posix is the specification for Unix like functions. The ESP-IDF provides many implementations for Posix functions but some are omitted and thus should not be used in ESP32 based applications. However there are times when we received 3rd party code that uses a Posix function that we don't have in our environment. Our choices then become: + +* Contact the 3rd party provider and ask them to alter their code to remove it, replace it or make it optional. +* Contact Espressif and ask them to add support for the missing Posix function. +* Provide a shim that satisfies the Posix function using the capabailities that are available to us. + +In the source file called `posix_shims.c` we provide some of those shims. \ No newline at end of file diff --git a/posix/posix_shims.c b/posix/posix_shims.c new file mode 100644 index 00000000..5515e250 --- /dev/null +++ b/posix/posix_shims.c @@ -0,0 +1,18 @@ +#include +#include +#include + +/** + * @brief Provide a shim for the Posix access(2) function. + * @param [in] pathname The file to check. + * @param [in] mode The mode of access requested. + * @return 0 on success, -1 on error with errno set. + */ +int access(const char *pathname, int mode) { + struct stat statBuf; + if (stat(pathname, &statBuf) == -1) { + errno = ENOENT; + return -1; + } + return 0; // Indicate that all we have access to the file. +} // access From 1bc04029909d839e461d67f6f02a3ba2ece38d53 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 10:47:31 -0500 Subject: [PATCH 023/310] Fixes for #101 --- cpp_utils/BLERemoteCharacteristic.h | 3 ++- cpp_utils/BLERemoteDescriptor.h | 3 ++- cpp_utils/BLERemoteService.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 0b6750a6..6f23f497 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -38,6 +38,7 @@ class BLERemoteCharacteristic { bool canWriteNoResponse(); BLERemoteDescriptor* getDescriptor(BLEUUID uuid); std::map* getDescriptors(); + uint16_t getHandle(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); @@ -61,7 +62,7 @@ class BLERemoteCharacteristic { esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - uint16_t getHandle(); + BLERemoteService* getRemoteService(); void removeDescriptors(); void retrieveDescriptors(); diff --git a/cpp_utils/BLERemoteDescriptor.h b/cpp_utils/BLERemoteDescriptor.h index 89a2e221..c2cf3836 100644 --- a/cpp_utils/BLERemoteDescriptor.h +++ b/cpp_utils/BLERemoteDescriptor.h @@ -23,6 +23,7 @@ class BLERemoteCharacteristic; */ class BLERemoteDescriptor { public: + uint16_t getHandle(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); @@ -48,7 +49,7 @@ class BLERemoteDescriptor { BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); - uint16_t getHandle(); + }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 75262868..2007fa22 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -35,6 +35,7 @@ class BLERemoteService { std::map* getCharacteristics(); BLEClient* getClient(void); + uint16_t getHandle(); BLEUUID getUUID(void); std::string toString(void); @@ -48,7 +49,6 @@ class BLERemoteService { // Private methods void retrieveCharacteristics(void); - uint16_t getHandle(); esp_gatt_id_t* getSrvcId(void); uint16_t getStartHandle(); uint16_t getEndHandle(); From d8f4d7ae53ad3fc72882aeedad7a995c6cc49de2 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 13:16:17 -0500 Subject: [PATCH 024/310] Removal of un-needed semaphore --- cpp_utils/BLEService.cpp | 5 ++--- cpp_utils/BLEService.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 96624f2e..208e103f 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -222,7 +222,7 @@ void BLEService::handleGATTServerEvent( switch(event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status // - uint16_t attr_handle @@ -238,13 +238,11 @@ void BLEService::handleGATTServerEvent( ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", BLEUUID(param->add_char.char_uuid).toString().c_str()); dump(); - m_semaphoreAddCharEvt.give(); break; } pCharacteristic->setHandle(param->add_char.attr_handle); m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); - m_semaphoreAddCharEvt.give(); break; } // Reached the correct service. break; @@ -288,6 +286,7 @@ void BLEService::handleGATTServerEvent( } // Default } // Switch + // Invoke the GATTS handler in each of the associated characteristics. m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); } // handleGATTServerEvent diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 86d0776b..1bbf46e0 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -79,7 +79,6 @@ class BLEService { BLECharacteristic* m_lastCreatedCharacteristic; BLEServer* m_pServer; //FreeRTOS::Semaphore m_serializeMutex; - FreeRTOS::Semaphore m_semaphoreAddCharEvt = FreeRTOS::Semaphore("AddCharEvt"); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); BLEUUID m_uuid; From 240d94e6fcd1e2e290a784191f1694207f134259 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 18:20:37 -0500 Subject: [PATCH 025/310] Changes for enhancement #113 --- cpp_utils/BLEService.cpp | 1 + cpp_utils/BLEUtils.cpp | 40 +++++++++++++++++++++++++++ cpp_utils/BLEUtils.h | 58 ++++++++++++++++++++-------------------- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 208e103f..9ddf8a79 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -248,6 +248,7 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_ADD_CHAR_EVT + // ESP_GATTS_START_EVT // // start: diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 00bfd470..402fcc8d 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -288,6 +288,29 @@ static const member_t members_ids[] = { {0, "" } }; +typedef struct { + uint32_t assignedNumber; + std::string name; +} gattdescriptor_t; + +static const gattdescriptor_t g_descriptor_ids[] = { + {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"}, + { 0, "" } +}; typedef struct { uint32_t assignedNumber; @@ -1801,6 +1824,23 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID } // 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 (p->name.length() > 0) { + if (p->assignedNumber == descriptorUUID) { + return 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. diff --git a/cpp_utils/BLEUtils.h b/cpp_utils/BLEUtils.h index 96066af9..5ef6c378 100644 --- a/cpp_utils/BLEUtils.h +++ b/cpp_utils/BLEUtils.h @@ -20,42 +20,42 @@ */ class BLEUtils { public: - static const char* advTypeToString(uint8_t advType); - static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); - + static const char* addressTypeToString(esp_ble_addr_type_t type); + static const char* advTypeToString(uint8_t advType); + static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true); - static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); - static char* buildHexData(uint8_t *target, uint8_t *source, uint8_t length); - static BLEClient* findByConnId(uint16_t conn_id); - static BLEClient* findByAddress(BLEAddress address); - static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); - static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); - static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement); - static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); - static std::string gattStatusToString(esp_gatt_status_t status); - static std::string gattServiceToString(uint32_t serviceId); - static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); - static void registerByAddress(BLEAddress address, BLEClient* pDevice); - static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); - static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); - static std::string getMember(uint32_t memberId); - static std::string buildPrintData(uint8_t* source, size_t length); + static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); + static std::string buildPrintData(uint8_t* source, size_t length); + static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); + static const char* devTypeToString(esp_bt_dev_type_t type); + static void dumpGapEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); static void dumpGattClientEvent( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); static void dumpGattServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam); - static const char* devTypeToString(esp_bt_dev_type_t type); - static void dumpGapEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param); + static const char* eventTypeToString(esp_ble_evt_type_t eventType); + static BLEClient* findByAddress(BLEAddress address); + static BLEClient* findByConnId(uint16_t conn_id); static const char* gapEventToString(uint32_t eventType); + static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); + static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); + static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); + static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement); + static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); + static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); + static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); + static std::string gattServiceToString(uint32_t serviceId); + static std::string gattStatusToString(esp_gatt_status_t status); + static std::string getMember(uint32_t memberId); + static void registerByAddress(BLEAddress address, BLEClient* pDevice); + static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt); - static const char* addressTypeToString(esp_ble_addr_type_t type); - static const char *eventTypeToString(esp_ble_evt_type_t eventType); }; #endif // CONFIG_BT_ENABLED From adc302638a42e625a1bc91c752e231b0ccaaa02a Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 19:08:20 -0500 Subject: [PATCH 026/310] Fixes for #114 --- cpp_utils/BLEService.cpp | 19 +++++++++++++------ cpp_utils/BLEService.h | 9 +++++---- cpp_utils/BLEUtils.cpp | 24 +++++++++++++++++------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 9ddf8a79..9e7fa280 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -29,21 +29,24 @@ static const char* LOG_TAG = "BLEService"; // Tag for logging. /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(const char* uuid) : BLEService(BLEUUID(uuid)) { +BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { } /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(BLEUUID uuid) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_pServer = nullptr; +BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; //m_serializeMutex.setName("BLEService"); m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; } // BLEService @@ -62,7 +65,11 @@ void BLEService::executeCreate(BLEServer *pServer) { esp_gatt_srvc_id_t srvc_id; srvc_id.id.inst_id = 0; srvc_id.id.uuid = *m_uuid.getNative(); - esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, 10); + esp_err_t errRc = ::esp_ble_gatts_create_service( + getServer()->getGattsIf(), + &srvc_id, + m_numHandles // The maximum number of handles associated with the service. + ); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 1bbf46e0..9a93aff7 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -52,8 +52,8 @@ class BLECharacteristicMap { */ class BLEService { public: - BLEService(const char* uuid); - BLEService(BLEUUID uuid); + BLEService(const char* uuid, uint32_t numHandles=10); + BLEService(BLEUUID uuid, uint32_t numHandles=10); void addCharacteristic(BLECharacteristic* pCharacteristic); BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); @@ -79,9 +79,10 @@ class BLEService { BLECharacteristic* m_lastCreatedCharacteristic; BLEServer* m_pServer; //FreeRTOS::Semaphore m_serializeMutex; - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); BLEUUID m_uuid; + uint32_t m_numHandles; uint16_t getHandle(); BLECharacteristic* getLastCreatedCharacteristic(); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 402fcc8d..170fbf7b 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1542,13 +1542,23 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_ADD_CHAR_DESCR_EVT case ESP_GATTS_ADD_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[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()); + if (evtParam->add_char.status == ESP_GATT_OK) { + ESP_LOGD(LOG_TAG, "[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 { + ESP_LOGE(LOG_TAG, "[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 From e2bf94ab10238679d763f20cb271e57ccd0a0f93 Mon Sep 17 00:00:00 2001 From: kolban Date: Sat, 7 Oct 2017 19:15:11 -0500 Subject: [PATCH 027/310] More code for #114 --- cpp_utils/BLEServer.cpp | 5 +++-- cpp_utils/BLEServer.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index a7e61d46..4a8dfd57 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -66,9 +66,10 @@ BLEService* BLEServer::createService(const char* uuid) { * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition * of a new service. Every service must have a unique UUID. * @param [in] uuid The UUID of the new service. + * @param [in] numHandles The maximum number of handles associated with this service. * @return A reference to the new service object. */ -BLEService* BLEServer::createService(BLEUUID uuid) { +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); m_semaphoreCreateEvt.take("createService"); @@ -80,7 +81,7 @@ BLEService* BLEServer::createService(BLEUUID uuid) { return nullptr; } - BLEService* pService = new BLEService(uuid); + BLEService* pService = new BLEService(uuid, numHandles); m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index c7580d24..64b34b41 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -53,7 +53,7 @@ class BLEServer { public: uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks *pCallbacks); void startAdvertising(); From 1891195b996e87b9cf5e84711dd48fed85d8c295 Mon Sep 17 00:00:00 2001 From: FulminatingGhost Date: Sun, 8 Oct 2017 20:10:58 +0200 Subject: [PATCH 028/310] Added methods to Task class and moved a debug logging line. - Added a method "setPriority" that enables one to set the task priority. - Added a method "setName" that enables one to set the name of the Task outside the constructor. - Moved the task exit ESP_LOG call in front of the stop() call. It would otherwise never be called. --- cpp_utils/Task.cpp | 27 ++++++++++++++++++++++++--- cpp_utils/Task.h | 5 ++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index fe6ba2d0..da1a7bb8 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -24,9 +24,10 @@ static char tag[] = "Task"; * @param [in] stackSize The size of the stack. * @return N/A. */ -Task::Task(std::string taskName, uint16_t stackSize) { +Task::Task(std::string taskName, uint16_t stackSize, uint8_t priority) { m_taskName = taskName; m_stackSize = stackSize; + m_priority = priority; m_taskData = nullptr; m_handle = nullptr; } // Task @@ -55,8 +56,8 @@ void Task::runTask(void* pTaskInstance) { Task* pTask = (Task*)pTaskInstance; ESP_LOGD(tag, ">> runTask: taskName=%s", pTask->m_taskName.c_str()); pTask->run(pTask->m_taskData); - pTask->stop(); ESP_LOGD(tag, "<< runTask: taskName=%s", pTask->m_taskName.c_str()); + pTask->stop(); } // runTask /** @@ -70,7 +71,7 @@ void Task::start(void* taskData) { ESP_LOGW(tag, "Task::start - There might be a task already running!"); } m_taskData = taskData; - ::xTaskCreate(&runTask, m_taskName.c_str(), m_stackSize, this, 5, &m_handle); + ::xTaskCreate(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle); } // start @@ -97,3 +98,23 @@ void Task::stop() { void Task::setStackSize(uint16_t stackSize) { m_stackSize = stackSize; } // setStackSize + +/** + * @brief Set the priority of the task. + * + * @param [in] priority The priority for the task. + * @return N/A. + */ +void Task::setPriority(uint8_t priority) { + m_priority = priority; +} // setPriority + +/** + * @brief Set the name of the task. + * + * @param [in] name The name for the task. + * @return N/A. + */ +void Task::setName(std::string name) { + m_taskName = name; +} // setName diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 51e18fc8..21582896 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -33,9 +33,11 @@ */ class Task { public: - Task(std::string taskName="Task", uint16_t stackSize=10000); + Task(std::string taskName="Task", uint16_t stackSize=10000, uint8_t priority=5); virtual ~Task(); void setStackSize(uint16_t stackSize); + void setPriority(uint8_t priority); + void setName(std::string name); void start(void* taskData=nullptr); void stop(); /** @@ -56,6 +58,7 @@ class Task { static void runTask(void *data); std::string m_taskName; uint16_t m_stackSize; + uint8_t m_priority; }; #endif /* COMPONENTS_CPP_UTILS_TASK_H_ */ From 1879d725911f2ec490539e9df4becb59f4b24679 Mon Sep 17 00:00:00 2001 From: FulminatingGhost Date: Sun, 8 Oct 2017 20:27:45 +0200 Subject: [PATCH 029/310] Changed tabs to spaces ... --- cpp_utils/Task.cpp | 2 +- cpp_utils/Task.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index da1a7bb8..4520478a 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -27,7 +27,7 @@ static char tag[] = "Task"; Task::Task(std::string taskName, uint16_t stackSize, uint8_t priority) { m_taskName = taskName; m_stackSize = stackSize; - m_priority = priority; + m_priority = priority; m_taskData = nullptr; m_handle = nullptr; } // Task diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 21582896..74d7f332 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -58,7 +58,7 @@ class Task { static void runTask(void *data); std::string m_taskName; uint16_t m_stackSize; - uint8_t m_priority; + uint8_t m_priority; }; #endif /* COMPONENTS_CPP_UTILS_TASK_H_ */ From 7a4c880868e1147b6815be6f1393832c4390b3a1 Mon Sep 17 00:00:00 2001 From: kolban Date: Mon, 9 Oct 2017 08:53:05 -0500 Subject: [PATCH 030/310] Change requested for #92 --- cpp_utils/BLEUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 170fbf7b..a91bbcc9 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -285,6 +285,7 @@ static const member_t members_ids[] = { {0xFEFD, "Gimbal, Inc."}, {0xFEFE, "GN ReSound A/S"}, {0xFEFF, "GN Netcom"}, + {0xFFFF, "Reserved"}, /*for testing purposes only*/ {0, "" } }; From 2920d11821e0e3b85316730bf609d90449c271ab Mon Sep 17 00:00:00 2001 From: kolban Date: Tue, 10 Oct 2017 18:33:20 -0500 Subject: [PATCH 031/310] Changes for #119 and others --- cpp_utils/BLEAdvertising.cpp | 22 +- cpp_utils/BLEAdvertising.h | 1 + cpp_utils/BLEUUID.cpp | 95 +++--- cpp_utils/CPPNVS.cpp | 76 ++++- cpp_utils/CPPNVS.h | 8 +- cpp_utils/GeneralUtils.cpp | 8 +- cpp_utils/GeneralUtils.h | 5 +- cpp_utils/HttpRequest.cpp | 60 ++++ cpp_utils/HttpRequest.h | 2 + cpp_utils/HttpServer.cpp | 33 +- cpp_utils/HttpServer.h | 6 +- cpp_utils/SockServ.cpp | 64 ++-- cpp_utils/SockServ.h | 4 +- cpp_utils/Socket.cpp | 8 +- cpp_utils/Socket.h | 8 + cpp_utils/WiFi.cpp | 67 ++-- cpp_utils/WiFi.h | 7 +- networking/bootwifi/BootWiFi.cpp | 273 +++++++++++++++++ networking/bootwifi/BootWiFi.h | 32 ++ networking/bootwifi/README.md | 6 +- networking/bootwifi/bootwifi.c | 511 ------------------------------- networking/bootwifi/bootwifi.h | 15 - networking/bootwifi/component.mk | 8 +- 23 files changed, 657 insertions(+), 662 deletions(-) create mode 100644 networking/bootwifi/BootWiFi.cpp create mode 100644 networking/bootwifi/BootWiFi.h delete mode 100644 networking/bootwifi/bootwifi.c delete mode 100644 networking/bootwifi/bootwifi.h diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 049aa4dc..c1e0b30b 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -94,24 +94,10 @@ void BLEAdvertising::setServiceUUID(const char* serviceUUID) { void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { ESP_LOGD(LOG_TAG, ">> setServiceUUID - %s", serviceUUID.toString().c_str()); m_serviceUUID = serviceUUID; // Save the new service UUID - esp_bt_uuid_t* espUUID = m_serviceUUID.getNative(); - switch(espUUID->len) { - case ESP_UUID_LEN_16: { - m_advDataScanResponse.service_uuid_len = 2; - m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid16); - break; - } - case ESP_UUID_LEN_32: { - m_advDataScanResponse.service_uuid_len = 4; - m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid32); - break; - } - case ESP_UUID_LEN_128: { - m_advDataScanResponse.service_uuid_len = 16; - m_advDataScanResponse.p_service_uuid = reinterpret_cast(&espUUID->uuid.uuid128); - break; - } - } // switch + m_serviceUUID128 = serviceUUID.to128(); + + m_advDataScanResponse.service_uuid_len = 16; + m_advDataScanResponse.p_service_uuid = reinterpret_cast(&m_serviceUUID128.getNative()->uuid.uuid128); ESP_LOGD(LOG_TAG, "<< setServiceUUID"); } // setServiceUUID diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index afe761d8..810431e7 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -30,6 +30,7 @@ class BLEAdvertising { esp_ble_adv_data_t m_advDataScanResponse; esp_ble_adv_params_t m_advParams; BLEUUID m_serviceUUID; + BLEUUID m_serviceUUID128; }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 0cffe041..19da5185 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -66,15 +66,18 @@ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { BLEUUID::BLEUUID(std::string value) { m_valueSet = true; if (value.length() == 2) { - m_uuid.len = ESP_UUID_LEN_16; + m_uuid.len = ESP_UUID_LEN_16; m_uuid.uuid.uuid16 = value[0] | (value[1] << 8); - } else if (value.length() == 4) { - m_uuid.len = ESP_UUID_LEN_32; + } + else if (value.length() == 4) { + m_uuid.len = ESP_UUID_LEN_32; m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); - } else if (value.length() == 16) { + } + else if (value.length() == 16) { m_uuid.len = ESP_UUID_LEN_128; memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); - } else if (value.length() == 36) { + } + else if (value.length() == 36) { // If the length of the string is 36 bytes then we will assume it is a long hex string in // UUID format. m_uuid.len = ESP_UUID_LEN_128; @@ -131,6 +134,7 @@ BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { m_valueSet = true; } // BLEUUID + /** * @brief Create a UUID from the 16bit value. * @@ -278,50 +282,67 @@ BLEUUID BLEUUID::to128() { } // to128 -//01234567 8901 2345 6789 012345678901 -//0000180d-0000-1000-8000-00805f9b34fb -//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + /** * @brief Get a string representation of the UUID. * + * The format of a string is: + * 01234567 8901 2345 6789 012345678901 + * 0000180d-0000-1000-8000-00805f9b34fb + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * * @return A string representation of the UUID. */ std::string BLEUUID::toString() { - if (m_valueSet == false) { + if (m_valueSet == false) { // If we have no value, nothing to format. return ""; } - if (m_uuid.len == ESP_UUID_LEN_16) { - std::stringstream ss; - ss << "0000" << std::hex << std::setfill('0') << std::setw(4) << m_uuid.uuid.uuid16 << "-0000-1000-8000-00805f9b34fb"; - return ss.str(); - } - - if (m_uuid.len == ESP_UUID_LEN_32) { - std::stringstream ss; - ss << std::hex << std::setfill('0') << std::setw(8) << m_uuid.uuid.uuid32 << "-0000-1000-8000-00805f9b34fb"; - return ss.str(); - } - + // If the UUIDs are 16 or 32 bit, pad correctly. std::stringstream ss; + + if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. + ss << "0000" << + std::hex << + std::setfill('0') << + std::setw(4) << + m_uuid.uuid.uuid16 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // Return the string + } // End 16bit UUID + + if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. + ss << std::hex << + std::setfill('0') << + std::setw(8) << + m_uuid.uuid.uuid32 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // return the string + } // End 32bit UUID + + // The UUID is not 16bit or 32bit which means that it is 128bit. + // + // UUID string format: + // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP + // ss << std::hex << std::setfill('0') << - std::setw(2) << (int)m_uuid.uuid.uuid128[15] << - std::setw(2) << (int)m_uuid.uuid.uuid128[14] << - std::setw(2) << (int)m_uuid.uuid.uuid128[13] << - std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[11] << - std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[9] << - std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[7] << - std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[5] << - std::setw(2) << (int)m_uuid.uuid.uuid128[4] << - std::setw(2) << (int)m_uuid.uuid.uuid128[3] << - std::setw(2) << (int)m_uuid.uuid.uuid128[2] << - std::setw(2) << (int)m_uuid.uuid.uuid128[1] << - std::setw(2) << (int)m_uuid.uuid.uuid128[0]; + std::setw(2) << (int)m_uuid.uuid.uuid128[15] << + std::setw(2) << (int)m_uuid.uuid.uuid128[14] << + std::setw(2) << (int)m_uuid.uuid.uuid128[13] << + std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[11] << + std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[9] << + std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[7] << + std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[5] << + std::setw(2) << (int)m_uuid.uuid.uuid128[4] << + std::setw(2) << (int)m_uuid.uuid.uuid128[3] << + std::setw(2) << (int)m_uuid.uuid.uuid128[2] << + std::setw(2) << (int)m_uuid.uuid.uuid128[1] << + std::setw(2) << (int)m_uuid.uuid.uuid128[0]; return ss.str(); } // toString #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index 734cc170..99588a43 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -6,9 +6,12 @@ */ #include "CPPNVS.h" - +#include +#include #include +const char* LOG_TAG = "CPPNVS"; + /** * @brief Constructor. * @@ -17,12 +20,12 @@ */ NVS::NVS(std::string name, nvs_open_mode openMode) { m_name = name; - nvs_open(name.c_str(), openMode, &m_handle); + ::nvs_open(name.c_str(), openMode, &m_handle); } // NVS NVS::~NVS() { - nvs_close(m_handle); + ::nvs_close(m_handle); } // ~NVS @@ -30,7 +33,7 @@ NVS::~NVS() { * @brief Commit any work performed in the namespace. */ void NVS::commit() { - nvs_commit(m_handle); + ::nvs_commit(m_handle); } // commit @@ -38,7 +41,7 @@ void NVS::commit() { * @brief Erase ALL the keys in the namespace. */ void NVS::erase() { - nvs_erase_all(m_handle); + ::nvs_erase_all(m_handle); } // erase @@ -48,32 +51,79 @@ void NVS::erase() { * @param [in] key The key to erase from the namespace. */ void NVS::erase(std::string key) { - nvs_erase_key(m_handle, key.c_str()); + ::nvs_erase_key(m_handle, key.c_str()); } // erase /** - * @brief Retrieve a string value by key. + * @brief Retrieve a string/blob value by key. * * @param [in] key The key to read from the namespace. * @param [out] result The string read from the %NVS storage. */ -void NVS::get(std::string key, std::string* result) { +void NVS::get(std::string key, std::string* result, bool isBlob) { size_t length; - nvs_get_str(m_handle, key.c_str(), NULL, &length); + if (isBlob) { + ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); + } else { + ::nvs_get_str(m_handle, key.c_str(), NULL, &length); + } char *data = (char *)malloc(length); - nvs_get_str(m_handle, key.c_str(), data, &length); + if (isBlob) { + ::nvs_get_blob(m_handle, key.c_str(), data, &length); + } else { + ::nvs_get_str(m_handle, key.c_str(), data, &length); + } *result = std::string(data); free(data); } // get +void NVS::get(std::string key, uint32_t& value) { + ::nvs_get_u32(m_handle, key.c_str(), &value); +} // get - uint32_t + + +void NVS::get(std::string key, uint8_t* result, size_t& length) { + ESP_LOGD(LOG_TAG, ">> get: key: %s, blob: inputSize: %d", key.c_str(), length); + esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), result, &length); + if (rc != ESP_OK) { + ESP_LOGD(LOG_TAG, "nvs_get_blob: %d", rc); + } + ESP_LOGD(LOG_TAG, "<< get: outputSize: %d", length); +} // get - blob + + + /** - * @brief Set the string value by key. + * @brief Set the string/blob value by key. * * @param [in] key The key to set from the namespace. * @param [in] data The value to set for the key. */ -void NVS::set(std::string key, std::string data) { - nvs_set_str(m_handle, key.c_str(), data.c_str()); +void NVS::set(std::string key, std::string data, bool isBlob) { + ESP_LOGD(LOG_TAG, ">> set: key: %s, string: value=%s", key.c_str(), data.c_str()); + if (isBlob) { + ::nvs_set_blob(m_handle, key.c_str(), data.data(), data.length()); + } else { + ::nvs_set_str(m_handle, key.c_str(), data.c_str()); + } + ESP_LOGD(LOG_TAG, "<< set"); } // set + + +void NVS::set(std::string key, uint32_t value) { + ESP_LOGD(LOG_TAG, ">> set: key: %s, u32: value=%d", key.c_str(), value); + ::nvs_set_u32(m_handle, key.c_str(), value); + ESP_LOGD(LOG_TAG, "<< set"); +} // set - uint32_t + + +void NVS::set(std::string key, uint8_t* data, size_t length) { + ESP_LOGD(LOG_TAG, ">> set: key: %s, blob: length=%d", key.c_str(), length); + esp_err_t rc = ::nvs_set_blob(m_handle, key.c_str(), data, length); + if (rc != ESP_OK) { + ESP_LOGD(LOG_TAG, "nvs_get_blob: %d", rc); + } + ESP_LOGD(LOG_TAG, "<< set"); +} // set (BLOB) diff --git a/cpp_utils/CPPNVS.h b/cpp_utils/CPPNVS.h index 6802ff99..755f551b 100644 --- a/cpp_utils/CPPNVS.h +++ b/cpp_utils/CPPNVS.h @@ -21,8 +21,12 @@ class NVS { void erase(); void erase(std::string key); - void get(std::string key, std::string *result); - void set(std::string key, std::string data); + void get(std::string key, std::string* result, bool isBlob=false); + void get(std::string key, uint8_t* result, size_t &length); + void get(std::string key, uint32_t& value); + void set(std::string key, std::string data, bool isBlob=false); + void set(std::string key, uint32_t value); + void set(std::string key, uint8_t* data, size_t length); private: std::string m_name; nvs_handle m_handle; diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 7f99b972..6bfde84f 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -7,6 +7,7 @@ #include "GeneralUtils.h" #include +#include #include #include #include @@ -403,4 +404,9 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "Unknown ESP_ERR error"; } // errorToString - +/** + * @brief Restart the ESP32. + */ +void GeneralUtils::restart() { + esp_restart(); +} // restart diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 9042d8a7..ce75e43b 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -18,12 +18,13 @@ class GeneralUtils { public: GeneralUtils(); virtual ~GeneralUtils(); - static void hexDump(const uint8_t *pData, uint32_t length); - static std::string ipToString(uint8_t *ip); static bool base64Encode(const std::string &in, std::string *out); static bool base64Decode(const std::string &in, std::string *out); static bool endsWith(std::string str, char c); static const char *errorToString(esp_err_t errCode); + static void hexDump(const uint8_t *pData, uint32_t length); + static std::string ipToString(uint8_t *ip); + static void restart(); }; #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index 4866f4ca..45655893 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -139,6 +139,9 @@ void HttpRequest::close() { } // close_cpp + + + /** * @brief Dump the HttpRequest for debugging purposes. */ @@ -276,6 +279,34 @@ bool HttpRequest::isWebsocket() { } // isWebsocket +/** + * @brief Parse the request message as a form. + * @return A map containing the names/values of the form elements that were found. + */ +std::map HttpRequest::parseForm() { + ESP_LOGD(LOG_TAG, ">> parseForm"); + std::map map; + // A form is composed of name=value pairs where each pair is separated with an "&" character. + // Our algorithm is to split all the pairs by "&" and then split all the name/value pairs by "=". + std::istringstream ss(getBody()); // Get the body of the request. + std::string currentEntry; + while (std::getline(ss, currentEntry, '&')) { // For each form entry. + ESP_LOGD(LOG_TAG, "Processing: %s", currentEntry.c_str()); // Debug + std::istringstream currentPair(currentEntry); // Prepare to parse the name=value string. + std::string name; // Declare the name variable. + std::string value; // Declare the value variable. + + std::getline(currentPair, name, '='); // Parse the current form entry into name/value. + currentPair >> value; // The value is what remains. + map[name] = urlDecode(value); // Decode the field which may have been encoded. + ESP_LOGD(LOG_TAG, " %s = %s", name.c_str(), map[name].c_str()); // Debug + // Add the form entry into the map. + } // Processed all form entries. + ESP_LOGD(LOG_TAG, "<< parseForm"); // Debug + return map; // Return the map of form entries. +} // parseForm + + /** * @brief Return the constituent parts of the path. * If we imagine a path as composed of parts separated by slashes, then this function @@ -309,3 +340,32 @@ std::vector HttpRequest::pathSplit() { return ret; } // pathSplit + +/** + * @brief Decode a URL/form + * @param [in] str + * @return The decoded string. + */ +std::string HttpRequest::urlDecode(std::string str) { + // https://stackoverflow.com/questions/154536/encode-decode-urls-in-c + std::string ret; + char ch; + int i, ii, len = str.length(); + + for (i=0; i < len; i++){ + if (str[i] != '%'){ + if (str[i] == '+') { + ret += ' '; + } + else { + ret += str[i]; + } + } else { + sscanf(str.substr(i + 1, 2).c_str(), "%x", &ii); + ch = static_cast(ii); + ret += ch; + i = i + 2; + } + } + return ret; +} // urlDecode diff --git a/cpp_utils/HttpRequest.h b/cpp_utils/HttpRequest.h index 246da4b4..3f989cbc 100644 --- a/cpp_utils/HttpRequest.h +++ b/cpp_utils/HttpRequest.h @@ -65,7 +65,9 @@ class HttpRequest { WebSocket* getWebSocket(); // Get the WebSocket reference if this is a web socket. bool isClosed(); // Has the connection been closed? bool isWebsocket(); // Is this request to create a web socket? + std::map parseForm(); // Parse the body as a form. std::vector pathSplit(); + std::string urlDecode(std::string str); // Decode a URL. }; #endif /* COMPONENTS_CPP_UTILS_HTTPREQUEST_H_ */ diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index b6da6c1a..168941ad 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -180,18 +180,21 @@ class HttpServerTask: public Task { */ void run(void* data) { m_pHttpServer = (HttpServer*)data; // The passed in data is an instance of an HttpServer. - - SockServ sockServ(m_pHttpServer->getPort()); // Create a socket server on our target port. - sockServ.setSSL(m_pHttpServer->getSSL()); - sockServ.start(); // Start the socket server listening. - + m_pHttpServer->m_sockServ.setPort(m_pHttpServer->m_portNumber); + m_pHttpServer->m_sockServ.setSSL(m_pHttpServer->m_useSSL); + m_pHttpServer->m_sockServ.start(); ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort()); - + Socket clientSocket; while(1) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); - Socket clientSocket = sockServ.waitForNewClient(); // Block waiting for a new external client connection. + try { + clientSocket = m_pHttpServer->m_sockServ.waitForNewClient(); // Block waiting for a new external client connection. + } + catch(std::exception e) { + return; + } ESP_LOGD("HttpServerTask", "HttpServer listening on port %d received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); @@ -299,14 +302,30 @@ void HttpServer::setRootPath(std::string path) { * @param [in] useSSL Should we use SSL? */ void HttpServer::start(uint16_t portNumber, bool useSSL) { + // Design: + // The start of the HTTP server should be as fast as possible. ESP_LOGD(LOG_TAG, ">> start: port: %d, useSSL: %d", portNumber, useSSL); m_useSSL = useSSL; m_portNumber = portNumber; + HttpServerTask* pHttpServerTask = new HttpServerTask("HttpServerTask"); pHttpServerTask->start(this); } // start +/** + * @brief Shutdown the HTTP server. + */ +void HttpServer::stop() { + // Shutdown the HTTP Server. The high level is that we will stop the server socket + // that is listening for incoming connections. That will then shutdown all the other + // activities. + ESP_LOGD(LOG_TAG, ">> stop"); + m_sockServ.stop(); + ESP_LOGD(LOG_TAG, "<< stop"); +} // stop + + /** * @brief Construct an instance of a PathHandler. * diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index 0f3e6f5a..35d13383 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -11,11 +11,13 @@ #ifndef COMPONENTS_CPP_UTILS_HTTPSERVER_H_ #define COMPONENTS_CPP_UTILS_HTTPSERVER_H_ #include -#include + #include #include "SockServ.h" #include "HttpRequest.h" #include "HttpResponse.h" +#include "SockServ.h" +#include class HttpServerTask; @@ -61,6 +63,7 @@ class HttpServer { void setDirectoryListing(bool use); // Should we list the content of directories? void setRootPath(std::string path); // Set the root of the file system path. void start(uint16_t portNumber, bool useSSL=false); + void stop(); // Stop a previously started server. private: friend class HttpServerTask; friend class WebSocket; @@ -69,6 +72,7 @@ class HttpServer { std::string m_rootPath; // Root path into the file system. bool m_useSSL; // Is this server listening on an HTTPS port? bool m_directoryListing; // Should we list directory content? + SockServ m_sockServ; // Server socket. }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index d18283a9..7c7bcf99 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -33,30 +33,50 @@ SockServ::SockServ(uint16_t port) : SockServ() { } // SockServ + +/** + * Constructor + */ SockServ::SockServ() { - m_port = 0; - m_clientSemaphore.take("SockServ"); + m_port = 0; // Unknown port. m_acceptQueue = xQueueCreate(10, sizeof(Socket)); + m_useSSL = false; + m_clientSemaphore.take("SockServ"); // Create the queue; deleted in the destructor. } // SockServ + +/** + * Destructor + */ +SockServ::~SockServ() { + vQueueDelete(m_acceptQueue); // Delete the queue created in the constructor. +} // ~SockServ + + /** * @brief Accept an incoming connection. * @private * - * Block waiting for an incoming connection and accept it when it arrives. + * Block waiting for an incoming connection and accept it when it arrives. The new + * socket is placed on a queue and a semaphore signaled that a new client is available. */ -void SockServ::acceptTask(void *data) { - +/* static */ void SockServ::acceptTask(void* data) { SockServ* pSockServ = (SockServ*)data; - while(1) { - Socket tempSock = pSockServ->m_serverSocket.accept(pSockServ->getSSL()); - if (!tempSock.isValid()) { - continue; + try { + while(1) { + Socket tempSock = pSockServ->m_serverSocket.accept(pSockServ->getSSL()); + if (!tempSock.isValid()) { + continue; + } + + pSockServ->m_clientSet.insert(tempSock); + xQueueSendToBack(pSockServ->m_acceptQueue, &tempSock, portMAX_DELAY); + pSockServ->m_clientSemaphore.give(); } - - pSockServ->m_clientSet.insert(tempSock); - xQueueSendToBack(pSockServ->m_acceptQueue, &tempSock, portMAX_DELAY); - pSockServ->m_clientSemaphore.give(); + } catch(std::exception e) { + ESP_LOGD(LOG_TAG, "acceptTask ending"); + pSockServ->m_clientSemaphore.give(); // Wake up any waiting clients. + FreeRTOS::deleteTask(); } } // acceptTask @@ -150,7 +170,7 @@ void SockServ::setSSL(bool use) { void SockServ::start() { assert(m_port != 0); //m_serverSocket.setSSL(m_useSSL); - m_serverSocket.listen(m_port); + m_serverSocket.listen(m_port); // Create a socket and start listening on it. ESP_LOGD(LOG_TAG, "Now listening on port %d", m_port); FreeRTOS::startTask(acceptTask, "acceptTask", this, 16*1024); } // start @@ -160,11 +180,14 @@ void SockServ::start() { * @brief Stop listening for new partner connections. */ void SockServ::stop() { + ESP_LOGD(LOG_TAG, ">> stop"); + // By closing the server socket, the task watching on accept() on that socket + // will throw an exception which will propagate a clean ending. + m_serverSocket.close(); // Close the server socket. + ESP_LOGD(LOG_TAG, "<< stop"); } // stop - - Socket SockServ::waitForData(std::set& socketSet) { fd_set readSet; int maxFd = -1; @@ -206,9 +229,14 @@ Socket SockServ::waitForData(std::set& socketSet) { */ Socket SockServ::waitForNewClient() { ESP_LOGD(LOG_TAG, ">> waitForNewClient") - m_clientSemaphore.wait("waitForNewClient"); + m_clientSemaphore.wait("waitForNewClient"); // Unlocked in acceptTask. + m_clientSemaphore.take("waitForNewClient"); Socket tempSocket; - xQueueReceive(m_acceptQueue, &tempSocket,portMAX_DELAY); + BaseType_t rc = xQueueReceive(m_acceptQueue, &tempSocket, 0); // Read the socket from the queue. + if (rc != pdPASS) { + ESP_LOGE(LOG_TAG, "No new client from SockServ!"); + throw new SocketException(0); + } ESP_LOGD(LOG_TAG, "<< waitForNewClient"); return tempSocket; } // waitForNewClient diff --git a/cpp_utils/SockServ.h b/cpp_utils/SockServ.h index b7e170da..e0ffee76 100644 --- a/cpp_utils/SockServ.h +++ b/cpp_utils/SockServ.h @@ -35,13 +35,15 @@ class SockServ { static void acceptTask(void*); uint16_t m_port; Socket m_serverSocket; - FreeRTOS::Semaphore m_clientSemaphore; + FreeRTOS::Semaphore m_clientSemaphore = FreeRTOS::Semaphore("clientSemaphore"); std::set m_clientSet; QueueHandle_t m_acceptQueue; bool m_useSSL; + public: SockServ(uint16_t port); SockServ(); + ~SockServ(); int connectedCount(); void disconnect(Socket s); bool getSSL(); diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index ce464b45..849c3ef1 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -65,9 +65,9 @@ Socket Socket::accept(bool useSSL) { socklen_t clientAddressLength = sizeof(clientAddress); int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr *)&clientAddress, &clientAddressLength); if (clientSockFD == -1) { + SocketException se(errno); ESP_LOGE(LOG_TAG, "accept(): %s, m_sock=%d", strerror(errno), m_sock); - Socket newSocket; - return newSocket; + throw se; } ESP_LOGD(LOG_TAG, " - accept: Received new client!: sockFd: %d", clientSockFD); @@ -609,3 +609,7 @@ SocketInputRecordStreambuf::int_type SocketInputRecordStreambuf::underflow() { setg(m_buffer, m_buffer, m_buffer + bytesRead); return traits_type::to_int_type(*gptr()); } // underflow + +SocketException::SocketException(int myErrno) { + m_errno = myErrno; +} diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 9f046f57..33cce989 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -35,6 +35,14 @@ #include #include #include +#include + +class SocketException: public std::exception { +public: + SocketException(int myErrno); +private: + int m_errno; +}; /** * @brief Encapsulate a socket. diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index ecbb76e0..78571eba 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -53,6 +53,7 @@ WiFi::WiFi() , netmask(0) , m_pWifiEventHandler(nullptr) { + m_eventLoopStarted = false; ::nvs_flash_init(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&config); @@ -157,7 +158,17 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool } - ESP_ERROR_CHECK(esp_event_loop_init(WiFi::eventHandler, this)); + + // If the event loop has already started then change the callback else + // start the event loop. + if (m_eventLoopStarted) { + esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. + } else { + ESP_ERROR_CHECK(esp_event_loop_init(WiFi::eventHandler, this)); // Initialze the event handler. + m_eventLoopStarted = true; + } + + //ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); @@ -374,25 +385,37 @@ std::vector WiFi::scan() { * @return N/A. */ void WiFi::startAP(const std::string& ssid, const std::string& password) { - ::nvs_flash_init(); - ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) ); - wifi_config_t apConfig; - ::memset(&apConfig, 0, sizeof(apConfig)); - ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); - apConfig.ap.ssid_len = ssid.size(); - ::memcpy(apConfig.ap.password, password.data(), password.size()); - apConfig.ap.channel = 0; - apConfig.ap.authmode = WIFI_AUTH_OPEN; - apConfig.ap.ssid_hidden = 0; - apConfig.ap.max_connection = 4; - apConfig.ap.beacon_interval = 100; - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &apConfig) ); - ESP_ERROR_CHECK( esp_wifi_start() ); + ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); + ::nvs_flash_init(); + ::tcpip_adapter_init(); + + // If we have already started the event loop, then change the handler otherwise + // start the event loop. + if (m_eventLoopStarted) { + esp_event_loop_set_cb(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + } + else { + ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); + m_eventLoopStarted = true; + } + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) ); + wifi_config_t apConfig; + ::memset(&apConfig, 0, sizeof(apConfig)); + ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); + apConfig.ap.ssid_len = ssid.size(); + ::memcpy(apConfig.ap.password, password.data(), password.size()); + apConfig.ap.channel = 0; + apConfig.ap.authmode = WIFI_AUTH_OPEN; + apConfig.ap.ssid_hidden = 0; + apConfig.ap.max_connection = 4; + apConfig.ap.beacon_interval = 100; + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &apConfig) ); + ESP_ERROR_CHECK( esp_wifi_start() ); + ESP_LOGD(LOG_TAG, "<< startAP"); } // startAP @@ -400,8 +423,10 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { * @brief Set the event handler to use to process detected events. * @param[in] wifiEventHandler The class that will be used to process events. */ -void WiFi::setWifiEventHandler(WiFiEventHandler *wifiEventHandler) { +void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { + ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); this->m_pWifiEventHandler = wifiEventHandler; + ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); } // setWifiEventHandler diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 3279f284..d087c032 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -105,11 +105,12 @@ class WiFiAPRecord { class WiFi { private: static esp_err_t eventHandler(void* ctx, system_event_t* event); - uint32_t ip; - uint32_t gw; - uint32_t netmask; + uint32_t ip; + uint32_t gw; + uint32_t netmask; WiFiEventHandler* m_pWifiEventHandler; uint8_t m_dnsCount=0; + bool m_eventLoopStarted; FreeRTOS::Semaphore m_gotIpEvt = FreeRTOS::Semaphore("GotIpEvt"); public: diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp new file mode 100644 index 00000000..ab9f4f3d --- /dev/null +++ b/networking/bootwifi/BootWiFi.cpp @@ -0,0 +1,273 @@ +/** + * Bootwifi - Boot the WiFi environment. + * + * Compile with -DBOOTWIFI_OVERRIDE_GPIO= where is a GPIO pin number + * to use a GPIO override. + * See the README.md for full information. + * + */ +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +#include "BootWiFi.h" +#include "sdkconfig.h" +#include "selectAP.h" + +// If the structure of a record saved for a subsequent reboot changes +// then consider using semver to change the version number or else +// we may try and boot with the wrong data. +#define KEY_VERSION "version" +uint32_t g_version=0x0100; + +#define KEY_CONNECTION_INFO "connectionInfo" // Key used in NVS for connection info +#define BOOTWIFI_NAMESPACE "bootwifi" // Namespace in NVS for bootwifi +#define SSID_SIZE (32) // Maximum SSID size +#define PASSWORD_SIZE (64) // Maximum password size + + +/** + * The information about a WiFi access point connection. + */ +typedef struct { + char ssid[SSID_SIZE]; + char password[PASSWORD_SIZE]; + tcpip_adapter_ip_info_t ipInfo; // Optional static IP information +} connection_info_t; + +static bootwifi_callback_t g_callback = NULL; // Callback function to be invoked when we have finished. + + +// Forward declarations +static void saveConnectionInfo(connection_info_t *pConnectionInfo); + +static const char* LOG_TAG = "bootwifi"; + + +static void dumpConnectionInfo(connection_info_t *pConnectionInfo) { + ESP_LOGD(LOG_TAG, "connection_info.ssid = %.*s", SSID_SIZE, pConnectionInfo->ssid); + ESP_LOGD(LOG_TAG, "connection_info.password = %.*s", PASSWORD_SIZE, pConnectionInfo->password); +} + + +/** + * Retrieve the connection info. A rc==0 means ok. + */ +static int getConnectionInfo(connection_info_t *pConnectionInfo) { + ESP_LOGD(LOG_TAG, ">> getConnectionInfo"); + size_t size; + uint32_t version; + + NVS myNamespace(BOOTWIFI_NAMESPACE); + myNamespace.get(KEY_VERSION, version); + + // Check the versions match + if ((version & 0xff00) != (g_version & 0xff00)) { + ESP_LOGD(LOG_TAG, "Incompatible versions ... current is %x, found is %x", version, g_version); + return -1; + } + + size = sizeof(connection_info_t); + myNamespace.get(KEY_CONNECTION_INFO, (uint8_t*)pConnectionInfo, size); + + // Do a sanity check on the SSID + if (strlen(pConnectionInfo->ssid) == 0) { + ESP_LOGD(LOG_TAG, "NULL ssid detected"); + return -1; + } + dumpConnectionInfo(pConnectionInfo); + ESP_LOGD(LOG_TAG, "<< getConnectionInfo"); + return 0; + +} // getConnectionInfo + + +/** + * Save our connection info for retrieval on a subsequent restart. + */ +static void saveConnectionInfo(connection_info_t *pConnectionInfo) { + dumpConnectionInfo(pConnectionInfo); + NVS myNamespace(BOOTWIFI_NAMESPACE); + myNamespace.set(KEY_CONNECTION_INFO, (uint8_t*)pConnectionInfo, sizeof(connection_info_t)); + myNamespace.set(KEY_VERSION, g_version); + myNamespace.commit(); +} // setConnectionInfo + + +/** + * Retrieve the signal level on the OVERRIDE_GPIO pin. This is used to + * indicate that we should not attempt to connect to any previously saved + * access point we may know about. + */ + +static int checkOverrideGpio() { +#ifdef BOOTWIFI_OVERRIDE_GPIO + gpio_pad_select_gpio(BOOTWIFI_OVERRIDE_GPIO); + gpio_set_direction(BOOTWIFI_OVERRIDE_GPIO, GPIO_MODE_INPUT); + gpio_set_pull_mode(BOOTWIFI_OVERRIDE_GPIO, GPIO_PULLDOWN_ONLY); + return gpio_get_level(BOOTWIFI_OVERRIDE_GPIO); +#else + return 0; // If no boot override, return false +#endif +} // checkOverrideGpio + +static void sendForm(HttpRequest* pRequest, HttpResponse* pResponse) { + pResponse->setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + pResponse->addHeader(HttpRequest::HTTP_HEADER_CONTENT_TYPE, "text/html"); + pResponse->sendData(std::string((char*)selectAP_html, selectAP_html_len)); + pResponse->close(); +} // sendForm + +static void copyData(uint8_t* pTarget, size_t targetLength, std::string source) { + memset(pTarget, 0, targetLength); + size_t copySize = (source.length() > targetLength)? targetLength:source.length(); + memcpy(pTarget, source.data(), copySize); + if (copySize < targetLength) { + pTarget[copySize] = '\0'; + } +} + +static void processForm(HttpRequest* pRequest, HttpResponse* pResponse) { + ESP_LOGD(LOG_TAG, ">> processForm"); + std::map formMap = pRequest->parseForm(); + connection_info_t connectionInfo; + copyData((uint8_t*)connectionInfo.ssid, SSID_SIZE, formMap["ssid"]); + copyData((uint8_t*)connectionInfo.password, PASSWORD_SIZE, formMap["password"]); + + try { + inet_pton(AF_INET, formMap["ip"].c_str(), &connectionInfo.ipInfo.ip); + } catch(std::out_of_range e) { + connectionInfo.ipInfo.ip.addr = 0; + } + + try { + inet_pton(AF_INET, formMap["gw"].c_str(), &connectionInfo.ipInfo.gw); + } catch(std::out_of_range e) { + connectionInfo.ipInfo.gw.addr = 0; + } + + try { + inet_pton(AF_INET, formMap["netmask"].c_str(), &connectionInfo.ipInfo.netmask); + } catch(std::out_of_range e) { + connectionInfo.ipInfo.netmask.addr = 0; + } + + ESP_LOGD(LOG_TAG, "ssid: %s, password: %s", connectionInfo.ssid, connectionInfo.password); + + saveConnectionInfo(&connectionInfo); + + pResponse->setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + pResponse->addHeader(HttpRequest::HTTP_HEADER_CONTENT_TYPE, "text/html"); + //pResponse->sendData(std::string((char*)selectAP_html, selectAP_html_len)); + pResponse->close(); + FreeRTOS::sleep(500); + GeneralUtils::restart(); + ESP_LOGD(LOG_TAG, "<< processForm"); +} // processForm + + +class BootWifiEventHandler: public WiFiEventHandler { +public: + BootWifiEventHandler(BootWiFi *pBootWiFi) { + m_pBootWiFi = pBootWiFi; + } + + /** + * When the access point logic has started and we are able to receive incoming browser + * requests, start the internal HTTP Server. + */ + esp_err_t apStart() { + ESP_LOGD("BootWifiEventHandler", ">> apStart"); + if (m_pBootWiFi->m_httpServerStarted == false) { + m_pBootWiFi->m_httpServerStarted = true; + m_pBootWiFi->m_httpServer.addPathHandler("GET", "/", sendForm); + m_pBootWiFi->m_httpServer.addPathHandler("POST", "/ssidSelected", processForm); + m_pBootWiFi->m_httpServer.start(80); + } + ESP_LOGD("BootWifiEventHandler", "<< apStart"); + return ESP_OK; + } // apStaConnected + + /** + * If we fail to connect as a station, then become an access point and then + * become a web server. + */ + esp_err_t staDisconnected() { + ESP_LOGD("BootWifiEventHandler", ">> staDisconnected"); + m_pBootWiFi->m_wifi.startAP("Duktape", "Duktape"); + ESP_LOGD("BootWifiEventHandler", "<< staDisconnected"); + return ESP_OK; + } + +private: + BootWiFi *m_pBootWiFi; +}; + + +void BootWiFi::bootWiFi2() { + ESP_LOGD(LOG_TAG, ">> bootWiFi2"); + // Check for a GPIO override which occurs when a physical Pin is high + // during the test. This can force the ability to check for new configuration + // even if the existing configured access point is available. + m_wifi.setWifiEventHandler(new BootWifiEventHandler(this)); + if (checkOverrideGpio()) { + ESP_LOGD(LOG_TAG, "- GPIO override detected"); + m_wifi.startAP("Duktape", "Duktape"); + } else { + // There was NO GPIO override, proceed as normal. This means we retrieve + // our stored access point information of the access point we should connect + // against. If that information doesn't exist, then again we become an + // access point ourselves in order to allow a client to connect and bring + // up a browser. + connection_info_t connectionInfo; + int rc = getConnectionInfo(&connectionInfo); + if (rc == 0) { + // We have received connection information, let us now become a station + // and attempt to connect to the access point. + ESP_LOGD(LOG_TAG, "- Connecting to access point \"%s\" ...", connectionInfo.ssid); + assert(strlen(connectionInfo.ssid) > 0); + + m_wifi.setIPInfo( + connectionInfo.ipInfo.ip.addr, + connectionInfo.ipInfo.gw.addr, + connectionInfo.ipInfo.netmask.addr + ); + m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password); // Connect to the access point. + + } else { + // We do NOT have connection information. Let us now become an access + // point that serves up a web server and allow a browser user to specify + // the details that will be eventually used to allow us to connect + // as a station. + m_wifi.startAP("Duktape", "Duktape"); + } // We do NOT have connection info + } + ESP_LOGD(LOG_TAG, "<< bootWiFi2"); +} // bootWiFi2 + + + +void BootWiFi::boot() { + ESP_LOGD(LOG_TAG, ">> boot"); + ESP_LOGD(LOG_TAG, " +----------+"); + ESP_LOGD(LOG_TAG, " | BootWiFi |"); + ESP_LOGD(LOG_TAG, " +----------+"); + m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. + bootWiFi2(); + m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. + ESP_LOGD(LOG_TAG, "<< boot"); +} + +BootWiFi::BootWiFi() { + m_httpServerStarted = false; +} diff --git a/networking/bootwifi/BootWiFi.h b/networking/bootwifi/BootWiFi.h new file mode 100644 index 00000000..e2b26f94 --- /dev/null +++ b/networking/bootwifi/BootWiFi.h @@ -0,0 +1,32 @@ +/* + * BootWiFi.h + * + * Created on: Nov 25, 2016 + * Author: kolban + */ + +#ifndef MAIN_BOOTWIFI_H_ +#define MAIN_BOOTWIFI_H_ + +#include +#include +#include + +typedef void (*bootwifi_callback_t)(int rc); +class BootWifiEventHandler; + +class BootWiFi { +private: + friend BootWifiEventHandler; + void bootWiFi2(); + WiFi m_wifi; + HttpServer m_httpServer; + bool m_httpServerStarted; + FreeRTOS::Semaphore m_completeSemaphore = FreeRTOS::Semaphore("completeSemaphore"); + +public: + BootWiFi(); + void boot(); +}; + +#endif /* MAIN_BOOTWIFI_H_ */ diff --git a/networking/bootwifi/README.md b/networking/bootwifi/README.md index b6c8bfc2..d46d43d4 100644 --- a/networking/bootwifi/README.md +++ b/networking/bootwifi/README.md @@ -31,13 +31,13 @@ however, [Cesanta](https://www.cesanta.com/), the makers of Mongoose are still w port to the ESP32 which is anticipated to be available before 2017 so we should really wait for that to become available. -##GPIO boot override +## GPIO boot override To enable the ability to specify a GPIO pin to override known station information, compile the code with `-DBOOTWIFI_OVERRIDE_GPIO=` when `` is a GPIO pin number. If the pin is high at startup, then it will override. The pin is configured as pull-down low so it need not be artificially held low. The default is no override pin. -##Future enhancements +## Future enhancements There is always room for enhancements: * Improve the web page shown to the user - Right now it is pretty basic and ideally could be @@ -55,7 +55,7 @@ dramatically improved. Features to be added include - Network SSID to use when being an access point. - Network password to use when being an access point (if any). -##Design and implementation notes +## Design and implementation notes The parameters for Bootwifi are stored in Non-Volatile Storage (NVS). The name space in NVS is "bootwifi". The keys are: diff --git a/networking/bootwifi/bootwifi.c b/networking/bootwifi/bootwifi.c deleted file mode 100644 index 7de7c2fd..00000000 --- a/networking/bootwifi/bootwifi.c +++ /dev/null @@ -1,511 +0,0 @@ -/** - * Bootwifi - Boot the WiFi environment. - * - * Compile with -DBOOTWIFI_OVERRIDE_GPIO= where is a GPIO pin number - * to use a GPIO override. - * See the README.md for full information. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "bootwifi.h" -#include "sdkconfig.h" -#include "selectAP.h" - -// If the structure of a record saved for a subsequent reboot changes -// then consider using semver to change the version number or else -// we may try and boot with the wrong data. -#define KEY_VERSION "version" -uint32_t g_version=0x0100; - -#define KEY_CONNECTION_INFO "connectionInfo" // Key used in NVS for connection info -#define BOOTWIFI_NAMESPACE "bootwifi" // Namespace in NVS for bootwifi -#define SSID_SIZE (32) // Maximum SSID size -#define PASSWORD_SIZE (64) // Maximum password size - -typedef struct { - char ssid[SSID_SIZE]; - char password[PASSWORD_SIZE]; - tcpip_adapter_ip_info_t ipInfo; // Optional static IP information -} connection_info_t; - -static bootwifi_callback_t g_callback = NULL; // Callback function to be invoked when we have finished. - -static int g_mongooseStarted = 0; // Has the mongoose server started? -static int g_mongooseStopRequest = 0; // Request to stop the mongoose server. - -// Forward declarations -static void saveConnectionInfo(connection_info_t *pConnectionInfo); -static void becomeAccessPoint(); -static void bootWiFi2(); - -static char tag[] = "bootwifi"; - - - - - -/** - * Convert a Mongoose event type to a string. Used for debugging. - */ -static char *mongoose_eventToString(int ev) { - static char temp[100]; - switch (ev) { - case MG_EV_CONNECT: - return "MG_EV_CONNECT"; - case MG_EV_ACCEPT: - return "MG_EV_ACCEPT"; - case MG_EV_CLOSE: - return "MG_EV_CLOSE"; - case MG_EV_SEND: - return "MG_EV_SEND"; - case MG_EV_RECV: - return "MG_EV_RECV"; - case MG_EV_HTTP_REQUEST: - return "MG_EV_HTTP_REQUEST"; - case MG_EV_MQTT_CONNACK: - return "MG_EV_MQTT_CONNACK"; - case MG_EV_MQTT_CONNACK_ACCEPTED: - return "MG_EV_MQTT_CONNACK"; - case MG_EV_MQTT_CONNECT: - return "MG_EV_MQTT_CONNECT"; - case MG_EV_MQTT_DISCONNECT: - return "MG_EV_MQTT_DISCONNECT"; - case MG_EV_MQTT_PINGREQ: - return "MG_EV_MQTT_PINGREQ"; - case MG_EV_MQTT_PINGRESP: - return "MG_EV_MQTT_PINGRESP"; - case MG_EV_MQTT_PUBACK: - return "MG_EV_MQTT_PUBACK"; - case MG_EV_MQTT_PUBCOMP: - return "MG_EV_MQTT_PUBCOMP"; - case MG_EV_MQTT_PUBLISH: - return "MG_EV_MQTT_PUBLISH"; - case MG_EV_MQTT_PUBREC: - return "MG_EV_MQTT_PUBREC"; - case MG_EV_MQTT_PUBREL: - return "MG_EV_MQTT_PUBREL"; - case MG_EV_MQTT_SUBACK: - return "MG_EV_MQTT_SUBACK"; - case MG_EV_MQTT_SUBSCRIBE: - return "MG_EV_MQTT_SUBSCRIBE"; - case MG_EV_MQTT_UNSUBACK: - return "MG_EV_MQTT_UNSUBACK"; - case MG_EV_MQTT_UNSUBSCRIBE: - return "MG_EV_MQTT_UNSUBSCRIBE"; - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: - return "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"; - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: - return "MG_EV_WEBSOCKET_HANDSHAKE_DONE"; - case MG_EV_WEBSOCKET_FRAME: - return "MG_EV_WEBSOCKET_FRAME"; - } - sprintf(temp, "Unknown event: %d", ev); - return temp; -} //eventToString - - -// Convert a Mongoose string type to a string. -static char *mgStrToStr(struct mg_str mgStr) { - char *retStr = (char *) malloc(mgStr.len + 1); - memcpy(retStr, mgStr.p, mgStr.len); - retStr[mgStr.len] = 0; - return retStr; -} // mgStrToStr - - -/** - * Handle mongoose events. These are mostly requests to process incoming - * browser requests. The ones we handle are: - * GET / - Send the enter details page. - * GET /set - Set the connection info (REST request). - * POST /ssidSelected - Set the connection info (HTML FORM). - */ -static void mongoose_event_handler(struct mg_connection *nc, int ev, void *evData) { - ESP_LOGD(tag, "- Event: %s", mongoose_eventToString(ev)); - switch (ev) { - case MG_EV_HTTP_REQUEST: { - struct http_message *message = (struct http_message *) evData; - char *uri = mgStrToStr(message->uri); - ESP_LOGD(tag, " - uri: %s", uri); - - if (strcmp(uri, "/set") ==0 ) { - connection_info_t connectionInfo; -//fix - saveConnectionInfo(&connectionInfo); - ESP_LOGD(tag, "- Set the new connection info to ssid: %s, password: %s", - connectionInfo.ssid, connectionInfo.password); - mg_send_head(nc, 200, 0, "Content-Type: text/plain"); - } if (strcmp(uri, "/") == 0) { - mg_send_head(nc, 200, sizeof(selectAP_html), "Content-Type: text/html"); - mg_send(nc, selectAP_html, sizeof(selectAP_html)); - } - // Handle /ssidSelected - // This is an incoming form with properties: - // * ssid - The ssid of the network to connect against. - // * password - the password to use to connect. - // * ip - Static IP address ... may be empty - // * gw - Static GW address ... may be empty - // * netmask - Static netmask ... may be empty - if(strcmp(uri, "/ssidSelected") == 0) { - // We have received a form page containing the details. The form body will - // contain: - // ssid=&password= - ESP_LOGD(tag, "- body: %.*s", message->body.len, message->body.p); - connection_info_t connectionInfo; - mg_get_http_var(&message->body, "ssid", connectionInfo.ssid, SSID_SIZE); - mg_get_http_var(&message->body, "password", connectionInfo.password, PASSWORD_SIZE); - - char ipBuf[20]; - if (mg_get_http_var(&message->body, "ip", ipBuf, sizeof(ipBuf)) > 0) { - inet_pton(AF_INET, ipBuf, &connectionInfo.ipInfo.ip); - } else { - connectionInfo.ipInfo.ip.addr = 0; - } - - if (mg_get_http_var(&message->body, "gw", ipBuf, sizeof(ipBuf)) > 0) { - inet_pton(AF_INET, ipBuf, &connectionInfo.ipInfo.gw); - } - else { - connectionInfo.ipInfo.gw.addr = 0; - } - - if (mg_get_http_var(&message->body, "netmask", ipBuf, sizeof(ipBuf)) > 0) { - inet_pton(AF_INET, ipBuf, &connectionInfo.ipInfo.netmask); - } - else { - connectionInfo.ipInfo.netmask.addr = 0; - } - - ESP_LOGD(tag, "ssid: %s, password: %s", connectionInfo.ssid, connectionInfo.password); - - mg_send_head(nc, 200, 0, "Content-Type: text/plain"); - saveConnectionInfo(&connectionInfo); - bootWiFi2(); - } // url is "/ssidSelected" - // Else ... unknown URL - else { - mg_send_head(nc, 404, 0, "Content-Type: text/plain"); - } - nc->flags |= MG_F_SEND_AND_CLOSE; - free(uri); - break; - } // MG_EV_HTTP_REQUEST - } // End of switch -} // End of mongoose_event_handler - - -// FreeRTOS task to start Mongoose. -static void mongooseTask(void *data) { - struct mg_mgr mgr; - struct mg_connection *connection; - - ESP_LOGD(tag, ">> mongooseTask"); - g_mongooseStopRequest = 0; // Unset the stop request since we are being asked to start. - - mg_mgr_init(&mgr, NULL); - - connection = mg_bind(&mgr, ":80", mongoose_event_handler); - - if (connection == NULL) { - ESP_LOGE(tag, "No connection from the mg_bind()."); - mg_mgr_free(&mgr); - ESP_LOGD(tag, "<< mongooseTask"); - vTaskDelete(NULL); - return; - } - mg_set_protocol_http_websocket(connection); - - // Keep processing until we are flagged that there is a stop request. - while (!g_mongooseStopRequest) { - mg_mgr_poll(&mgr, 1000); - } - - // We have received a stop request, so stop being a web server. - mg_mgr_free(&mgr); - g_mongooseStarted = 0; - - // Since we HAVE ended mongoose, time to invoke the callback. - if (g_callback) { - g_callback(1); - } - - ESP_LOGD(tag, "<< mongooseTask"); - vTaskDelete(NULL); - return; -} // mongooseTask - - -/** - * An ESP32 WiFi event handler. - * The types of events that can be received here are: - * - * SYSTEM_EVENT_AP_PROBEREQRECVED - * SYSTEM_EVENT_AP_STACONNECTED - * SYSTEM_EVENT_AP_STADISCONNECTED - * SYSTEM_EVENT_AP_START - * SYSTEM_EVENT_AP_STOP - * SYSTEM_EVENT_SCAN_DONE - * SYSTEM_EVENT_STA_AUTHMODE_CHANGE - * SYSTEM_EVENT_STA_CONNECTED - * SYSTEM_EVENT_STA_DISCONNECTED - * SYSTEM_EVENT_STA_GOT_IP - * SYSTEM_EVENT_STA_START - * SYSTEM_EVENT_STA_STOP - * SYSTEM_EVENT_WIFI_READY - */ -static esp_err_t esp32_wifi_eventHandler(void *ctx, system_event_t *event) { - // Your event handling code here... - switch(event->event_id) { - // When we have started being an access point, then start being a web server. - case SYSTEM_EVENT_AP_START: { // Handle the AP start event - tcpip_adapter_ip_info_t ip_info; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip_info); - ESP_LOGD(tag, "**********************************************"); - ESP_LOGD(tag, "* We are now an access point and you can point") - ESP_LOGD(tag, "* your browser to http://" IPSTR, IP2STR(&ip_info.ip)); - ESP_LOGD(tag, "**********************************************"); - // Start Mongoose ... - if (!g_mongooseStarted) - { - g_mongooseStarted = 1; - xTaskCreatePinnedToCore(&mongooseTask, "bootwifi_mongoose_task", 8000, NULL, 5, NULL, 0); - } - break; - } // SYSTEM_EVENT_AP_START - - // If we fail to connect to an access point as a station, become an access point. - case SYSTEM_EVENT_STA_DISCONNECTED: { - ESP_LOGD(tag, "Station disconnected started"); - // We think we tried to connect as a station and failed! ... become - // an access point. - becomeAccessPoint(); - break; - } // SYSTEM_EVENT_AP_START - - // If we connected as a station then we are done and we can stop being a - // web server. - case SYSTEM_EVENT_STA_GOT_IP: { - ESP_LOGD(tag, "********************************************"); - ESP_LOGD(tag, "* We are now connected and ready to do work!") - ESP_LOGD(tag, "* - Our IP address is: " IPSTR, IP2STR(&event->event_info.got_ip.ip_info.ip)); - ESP_LOGD(tag, "********************************************"); - g_mongooseStopRequest = 1; // Stop mongoose (if it is running). - // Invoke the callback if Mongoose has NOT been started ... otherwise - // we will invoke the callback when mongoose has ended. - if (!g_mongooseStarted) { - if (g_callback) { - g_callback(1); - } - } // Mongoose was NOT started - break; - } // SYSTEM_EVENT_STA_GOTIP - - default: // Ignore the other event types - break; - } // Switch event - - return ESP_OK; -} // esp32_wifi_eventHandler - - -/** - * Retrieve the connection info. A rc==0 means ok. - */ -static int getConnectionInfo(connection_info_t *pConnectionInfo) { - nvs_handle handle; - size_t size; - esp_err_t err; - uint32_t version; - err = nvs_open(BOOTWIFI_NAMESPACE, NVS_READWRITE, &handle); - if (err != 0) { - ESP_LOGE(tag, "nvs_open: %x", err); - return -1; - } - - // Get the version that the data was saved against. - err = nvs_get_u32(handle, KEY_VERSION, &version); - if (err != ESP_OK) { - ESP_LOGD(tag, "No version record found (%d).", err); - nvs_close(handle); - return -1; - } - - // Check the versions match - if ((version & 0xff00) != (g_version & 0xff00)) { - ESP_LOGD(tag, "Incompatible versions ... current is %x, found is %x", version, g_version); - nvs_close(handle); - return -1; - } - - size = sizeof(connection_info_t); - err = nvs_get_blob(handle, KEY_CONNECTION_INFO, pConnectionInfo, &size); - if (err != ESP_OK) { - ESP_LOGD(tag, "No connection record found (%d).", err); - nvs_close(handle); - return -1; - } - if (err != ESP_OK) { - ESP_LOGE(tag, "nvs_open: %x", err); - nvs_close(handle); - return -1; - } - - // Cleanup - nvs_close(handle); - - // Do a sanity check on the SSID - if (strlen(pConnectionInfo->ssid) == 0) { - ESP_LOGD(tag, "NULL ssid detected"); - return -1; - } - return 0; -} // getConnectionInfo - - -/** - * Save our connection info for retrieval on a subsequent restart. - */ -static void saveConnectionInfo(connection_info_t *pConnectionInfo) { - nvs_handle handle; - ESP_ERROR_CHECK(nvs_open(BOOTWIFI_NAMESPACE, NVS_READWRITE, &handle)); - ESP_ERROR_CHECK(nvs_set_blob(handle, KEY_CONNECTION_INFO, pConnectionInfo, - sizeof(connection_info_t))); - ESP_ERROR_CHECK(nvs_set_u32(handle, KEY_VERSION, g_version)); - ESP_ERROR_CHECK(nvs_commit(handle)); - nvs_close(handle); -} // setConnectionInfo - - -/** - * Become a station connecting to an existing access point. - */ -static void becomeStation(connection_info_t *pConnectionInfo) { - ESP_LOGD(tag, "- Connecting to access point \"%s\" ...", pConnectionInfo->ssid); - assert(strlen(pConnectionInfo->ssid) > 0); - - // If we have a static IP address information, use that. - if (pConnectionInfo->ipInfo.ip.addr != 0) { - ESP_LOGD(tag, " - using a static IP address of " IPSTR, IP2STR(&pConnectionInfo->ipInfo.ip)); - tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &pConnectionInfo->ipInfo); - } else { - tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); - } - - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA)); - wifi_config_t sta_config; - sta_config.sta.bssid_set = 0; - memcpy(sta_config.sta.ssid, pConnectionInfo->ssid, SSID_SIZE); - memcpy(sta_config.sta.password, pConnectionInfo->password, PASSWORD_SIZE); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - ESP_ERROR_CHECK(esp_wifi_connect()); -} // becomeStation - - -/** - * Become an access point. - */ -static void becomeAccessPoint() { - ESP_LOGD(tag, "- Starting being an access point ..."); - // We don't have connection info so be an access point! - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - wifi_config_t apConfig = { - .ap = { - .ssid="ESP32 Duktape", - .ssid_len=0, - .password="Duktape", - .channel=0, - .authmode=WIFI_AUTH_OPEN, - .ssid_hidden=0, - .max_connection=4, - .beacon_interval=100 - } - }; - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &apConfig)); - ESP_ERROR_CHECK(esp_wifi_start()); -} // becomeAccessPoint - - -/** - * Retrieve the signal level on the OVERRIDE_GPIO pin. This is used to - * indicate that we should not attempt to connect to any previously saved - * access point we may know about. - */ - -static int checkOverrideGpio() { -#ifdef BOOTWIFI_OVERRIDE_GPIO - gpio_pad_select_gpio(BOOTWIFI_OVERRIDE_GPIO); - gpio_set_direction(BOOTWIFI_OVERRIDE_GPIO, GPIO_MODE_INPUT); - gpio_set_pull_mode(BOOTWIFI_OVERRIDE_GPIO, GPIO_PULLDOWN_ONLY); - return gpio_get_level(BOOTWIFI_OVERRIDE_GPIO); -#else - return 0; // If no boot override, return false -#endif -} // checkOverrideGpio - - - -static void bootWiFi2() { - ESP_LOGD(tag, ">> bootWiFi2"); - // Check for a GPIO override which occurs when a physical Pin is high - // during the test. This can force the ability to check for new configuration - // even if the existing configured access point is available. - if (checkOverrideGpio()) { - ESP_LOGD(tag, "- GPIO override detected"); - becomeAccessPoint(); - } else { - // There was NO GPIO override, proceed as normal. This means we retrieve - // our stored access point information of the access point we should connect - // against. If that information doesn't exist, then again we become an - // access point ourselves in order to allow a client to connect and bring - // up a browser. - connection_info_t connectionInfo; - int rc = getConnectionInfo(&connectionInfo); - if (rc == 0) { - // We have received connection information, let us now become a station - // and attempt to connect to the access point. - becomeStation(&connectionInfo); - - } else { - // We do NOT have connection information. Let us now become an access - // point that serves up a web server and allow a browser user to specify - // the details that will be eventually used to allow us to connect - // as a station. - becomeAccessPoint(); - } // We do NOT have connection info - } - ESP_LOGD(tag, "<< bootWiFi2"); -} // bootWiFi2 - - -/** - * Main entry into bootWiFi - */ -void bootWiFi(bootwifi_callback_t callback) { - ESP_LOGD(tag, ">> bootWiFi"); - g_callback = callback; - nvs_flash_init(); - tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(esp32_wifi_eventHandler, NULL)); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - - bootWiFi2(); - - ESP_LOGD(tag, "<< bootWiFi"); -} // bootWiFi diff --git a/networking/bootwifi/bootwifi.h b/networking/bootwifi/bootwifi.h deleted file mode 100644 index 885da4a8..00000000 --- a/networking/bootwifi/bootwifi.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * bootwifi.h - * - * Created on: Nov 25, 2016 - * Author: kolban - */ - -#ifndef MAIN_BOOTWIFI_H_ -#define MAIN_BOOTWIFI_H_ - -typedef void (*bootwifi_callback_t)(int rc); -void bootWiFi(); - - -#endif /* MAIN_BOOTWIFI_H_ */ diff --git a/networking/bootwifi/component.mk b/networking/bootwifi/component.mk index e64cac1b..c00ba051 100644 --- a/networking/bootwifi/component.mk +++ b/networking/bootwifi/component.mk @@ -6,10 +6,4 @@ # in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # -CFLAGS += -DCS_PLATFORM=3 \ - -DMG_DISABLE_DIRECTORY_LISTING=1 \ - -DMG_DISABLE_DAV=1 \ - -DMG_DISABLE_CGI=1 \ - -DMG_DISABLE_FILESYSTEM=1 \ - -DMG_LWIP=1 \ - -DMG_ENABLE_BROADCAST \ No newline at end of file +COMPONENT_ADD_INCLUDEDIRS=. \ No newline at end of file From ff9368469868c090cb1172aad61e985f2e587e5a Mon Sep 17 00:00:00 2001 From: kolban Date: Tue, 10 Oct 2017 19:15:06 -0500 Subject: [PATCH 032/310] Code changes for #115 --- cpp_utils/BLEAdvertising.cpp | 105 +++++----- cpp_utils/BLEAdvertising.h | 9 +- cpp_utils/tests/BLETests/Sample1.cpp | 39 +++- .../tests/BLETests/SampleClientAndServer.cpp | 195 ++++++++++++++++++ .../tests/BLETests/SampleClientWithWiFi.cpp | 122 +++++++++++ cpp_utils/tests/BLETests/SampleDisconnect.cpp | 113 ++++++++++ cpp_utils/tests/BLETests/SampleServer.cpp | 3 +- cpp_utils/tests/BLETests/main.cpp | 33 +-- 8 files changed, 537 insertions(+), 82 deletions(-) create mode 100644 cpp_utils/tests/BLETests/SampleClientAndServer.cpp create mode 100644 cpp_utils/tests/BLETests/SampleClientWithWiFi.cpp create mode 100644 cpp_utils/tests/BLETests/SampleDisconnect.cpp diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index c1e0b30b..26479663 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -35,20 +35,6 @@ BLEAdvertising::BLEAdvertising() { m_advData.p_service_uuid = nullptr; m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); - m_advDataScanResponse.set_scan_rsp = true; - m_advDataScanResponse.include_name = false; - m_advDataScanResponse.include_txpower = false; - m_advDataScanResponse.min_interval = 0x20; - m_advDataScanResponse.max_interval = 0x40; - m_advDataScanResponse.appearance = 0x00; - m_advDataScanResponse.manufacturer_len = 0; - m_advDataScanResponse.p_manufacturer_data = nullptr; - m_advDataScanResponse.service_data_len = 0; - m_advDataScanResponse.p_service_data = nullptr; - m_advDataScanResponse.service_uuid_len = 0; - m_advDataScanResponse.p_service_uuid = nullptr; - m_advDataScanResponse.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); - m_advParams.adv_int_min = 0x20; m_advParams.adv_int_max = 0x40; m_advParams.adv_type = ADV_TYPE_IND; @@ -58,6 +44,24 @@ BLEAdvertising::BLEAdvertising() { } // BLEAdvertising +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void BLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(BLEUUID(serviceUUID)); +} // addServiceUUID + + /** * @brief Set the device appearance in the advertising data. * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: @@ -70,38 +74,6 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { } // setAppearance -/** - * @brief Set the service UUID. - * We maintain a class member called m_advData (esp_ble_adv_data_t) that is passed to the - * ESP-IDF advertising functions. In this method, we see two fields within that structure - * namely service_uuid_len and p_service_uuid to be the information supplied in the passed - * in service uuid. - * @param [in] uuid The UUID of the service. - * @return N/A. - */ -void BLEAdvertising::setServiceUUID(const char* serviceUUID) { - return setServiceUUID(BLEUUID(serviceUUID)); -} -/** - * @brief Set the service UUID. - * We maintain a class member called m_advData (esp_ble_adv_data_t) that is passed to the - * ESP-IDF advertising functions. In this method, we see two fields within that structure - * namely service_uuid_len and p_service_uuid to be the information supplied in the passed - * in service uuid. - * @param [in] uuid The UUID of the service. - * @return N/A. - */ -void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { - ESP_LOGD(LOG_TAG, ">> setServiceUUID - %s", serviceUUID.toString().c_str()); - m_serviceUUID = serviceUUID; // Save the new service UUID - m_serviceUUID128 = serviceUUID.to128(); - - m_advDataScanResponse.service_uuid_len = 16; - m_advDataScanResponse.p_service_uuid = reinterpret_cast(&m_serviceUUID128.getNative()->uuid.uuid128); - ESP_LOGD(LOG_TAG, "<< setServiceUUID"); -} // setServiceUUID - - /** * @brief Start advertising. * Start advertising. @@ -110,30 +82,49 @@ void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start"); - if (m_advDataScanResponse.service_uuid_len > 0) { - uint8_t hexData[16*2+1]; - BLEUtils::buildHexData(hexData, m_advDataScanResponse.p_service_uuid, m_advDataScanResponse.service_uuid_len); - ESP_LOGD(LOG_TAG, " - Service: service_uuid_len=%d, p_service_uuid=0x%x (data=%s)", - m_advDataScanResponse.service_uuid_len, - (uint32_t)m_advDataScanResponse.p_service_uuid, - (m_advDataScanResponse.service_uuid_len > 0?(char *)hexData:"N/A") - ); - } // We have a service to advertise + // We have a vector of service UUIDs that we wish to advertise. In order to use the + // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) + // representations. If we have 1 or more services to advertise then we allocate enough + // storage to host them and then copy them in one at a time into the contiguous storage. + int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { + m_advData.service_uuid_len = 16*numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + uint8_t* p = m_advData.p_service_uuid; + for (int i=0; iuuid.uuid128, 16); + p+=16; + } + } else { + m_advData.service_uuid_len = 0; + ESP_LOGD(LOG_TAG, "- no services advertised"); + } // Set the configuration for advertising. + m_advData.set_scan_rsp = false; esp_err_t errRc = ::esp_ble_gap_config_adv_data(&m_advData); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } - errRc = ::esp_ble_gap_config_adv_data(&m_advDataScanResponse); + m_advData.set_scan_rsp = true; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } + // If we had services to advertise then we previously allocated some storage for them. + // Here we release that storage. + if (m_advData.service_uuid_len > 0) { + delete[] m_advData.p_service_uuid; + m_advData.p_service_uuid = nullptr; + } + // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); if (errRc != ESP_OK) { @@ -158,4 +149,6 @@ void BLEAdvertising::stop() { } ESP_LOGD(LOG_TAG, "<< stop"); } // stop + + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 810431e7..6f315b8c 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -11,6 +11,7 @@ #if defined(CONFIG_BT_ENABLED) #include #include "BLEUUID.h" +#include /** * @brief Perform and manage %BLE advertising. @@ -20,17 +21,15 @@ class BLEAdvertising { public: BLEAdvertising(); + void addServiceUUID(BLEUUID serviceUUID); + void addServiceUUID(const char* serviceUUID); void start(); void stop(); void setAppearance(uint16_t appearance); - void setServiceUUID(const char* serviceUUID); - void setServiceUUID(BLEUUID serviceUUID); private: esp_ble_adv_data_t m_advData; - esp_ble_adv_data_t m_advDataScanResponse; esp_ble_adv_params_t m_advParams; - BLEUUID m_serviceUUID; - BLEUUID m_serviceUUID128; + std::vector m_serviceUUIDs; }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/cpp_utils/tests/BLETests/Sample1.cpp b/cpp_utils/tests/BLETests/Sample1.cpp index ad83ca7f..b551a186 100644 --- a/cpp_utils/tests/BLETests/Sample1.cpp +++ b/cpp_utils/tests/BLETests/Sample1.cpp @@ -2,6 +2,7 @@ #include "BLEServer.h" #include #include +#include #include "BLEDevice.h" #include "sdkconfig.h" @@ -9,30 +10,56 @@ // See the following for generating UUIDs: // https://www.uuidgenerator.net/ -#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" -#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +#define CHARACTERISTIC_UUID2 "54059634-9448-404f-9af4-7d14556f3ad8" +#define CHARACTERISTIC_UUID3 "78f8a814-7b20-40ca-b970-0aba448c53b1" +#define CHARACTERISTIC_UUID4 "03a55273-c1ef-4eab-a6c0-7ff11509122f" +#define CHARACTERISTIC_UUID5 "0d19566d-2144-4443-9779-19d42e283439" static void run() { BLEDevice::init("MYDEVICE"); - BLEServer *pServer = BLEDevice::createServer(); + BLEServer* pServer = BLEDevice::createServer(); - BLEService *pService = pServer->createService(BLEUUID(SERVICE_UUID)); + BLEService* pService = pServer->createService(BLEUUID(SERVICE_UUID)); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( + BLECharacteristic* pCharacteristic = pService->createCharacteristic( BLEUUID(CHARACTERISTIC_UUID), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID2), + BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID3), + BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID4), + BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID5), + BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); pService->start(); - BLEAdvertising *pAdvertising = pServer->getAdvertising(); + BLEAdvertising* pAdvertising = pServer->getAdvertising(); pAdvertising->start(); } void Sample1(void) { + //esp_log_level_set("*", ESP_LOG_ERROR); run(); } // app_main diff --git a/cpp_utils/tests/BLETests/SampleClientAndServer.cpp b/cpp_utils/tests/BLETests/SampleClientAndServer.cpp new file mode 100644 index 00000000..ecb028d9 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleClientAndServer.cpp @@ -0,0 +1,195 @@ +/** + * Create a sample BLE client that connects to a BLE server and then retrieves the current + * characteristic value. It will then periodically update the value of the characteristic on the + * remote server with the current time since boot. + */ +#include +#include +#include +#include +#include +#include +extern "C" { // See issue 1069: https://github.com/espressif/esp-idf/issues/1069 + #include +} +#include "BLEDevice.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClientAndServer"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MyServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting to be a BLE Server"); + + BLEDevice::init("MYDEVICE"); + BLEServer* pServer = BLEDevice::createServer(); + + BLEService* pService = pServer->createService(serviceUUID); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + charUUID, + BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->setValue("Hello World!"); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(pService->getUUID()); + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (!advertisedDevice.haveServiceUUID()) { + ESP_LOGD(LOG_TAG, "Found device is not advertising a service ... ignoring"); + return; + } + if (advertisedDevice.getServiceUUID().equals(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + return; + } // Found our server + ESP_LOGD(LOG_TAG, "Found device advertised %s which is not %s ... ignoring", + advertisedDevice.getServiceUUID().toString().c_str(), + serviceUUID.toString().c_str()); + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClientAndServer(void) { + ESP_LOGD(LOG_TAG, "SampleClientAndServer starting"); + esp_vfs_dev_uart_register(); + int fd = open("/dev/uart/0", O_RDONLY); + if (fd == -1) { + ESP_LOGE(LOG_TAG, "Failed to open file %s", strerror(errno)); + return; + } + + ESP_LOGD(LOG_TAG, "Enter:"); + ESP_LOGD(LOG_TAG, "C - Client"); + ESP_LOGD(LOG_TAG, "S - Server"); + bool isServer; + while(1) { + uint8_t val; + ssize_t rc; + // Read a character from the UART. Since the UART is non-blocking, we loop until we have + // a character available. + do { + rc = read(fd, &val, 1); + if (rc == -1) { + //ESP_LOGE(LOG_TAG, "Failed to read file %s", strerror(errno)); + FreeRTOS::sleep(100); + //return; + } + } while(rc == -1); + + // See if the character is an indication of being a server or a client. + + if (val == 'c' || val == 'C') { + isServer = false; + break; + } + if (val == 's' || val == 'S') { + isServer = true; + break; + } + } + close(fd); // Close the UART file as we don't need it any more. + ESP_LOGD(LOG_TAG, "Chosen: %s", isServer?"Server":"Client"); + + if (isServer) { + MyServer* pMyServer = new MyServer(); + pMyServer->setStackSize(20000); + pMyServer->start(); + } else { + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(15); + } + +} // SampleClient diff --git a/cpp_utils/tests/BLETests/SampleClientWithWiFi.cpp b/cpp_utils/tests/BLETests/SampleClientWithWiFi.cpp new file mode 100644 index 00000000..152ff130 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleClientWithWiFi.cpp @@ -0,0 +1,122 @@ +/** + * Create a sample BLE client that connects to a BLE server and then retrieves the current + * characteristic value. It will then periodically update the value of the characteristic on the + * remote server with the current time since boot. + */ +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" +#include "WiFi.h" +#include "HttpServer.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClientWithWiFi"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClientWithWiFi(void) { + ESP_LOGD(LOG_TAG, "SampleClientWithWiFi starting"); + WiFi wifi; + wifi.setIPInfo("192.168.1.99", "192.168.1.1", "255.255.255.0"); + wifi.connectAP("sweetie", "l16wint!"); + + HttpServer httpServer; + httpServer.start(80); + + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(15); +} // SampleClient diff --git a/cpp_utils/tests/BLETests/SampleDisconnect.cpp b/cpp_utils/tests/BLETests/SampleDisconnect.cpp new file mode 100644 index 00000000..88869075 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleDisconnect.cpp @@ -0,0 +1,113 @@ +/** + * Create a sample BLE client that connects to a BLE server and then retrieves the current + * characteristic value. It will then periodically update the value of the characteristic on the + * remote server with the current time since boot. + */ +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClientDisconnect"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + for (int i=0; i<5; i++) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClientDisconnect(void) { + ESP_LOGD(LOG_TAG, "Scanning SampleClientDisconnect starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(15); +} // SampleClient diff --git a/cpp_utils/tests/BLETests/SampleServer.cpp b/cpp_utils/tests/BLETests/SampleServer.cpp index 15a52cd6..c7dffd1a 100644 --- a/cpp_utils/tests/BLETests/SampleServer.cpp +++ b/cpp_utils/tests/BLETests/SampleServer.cpp @@ -39,7 +39,8 @@ class MainBLEServer: public Task { pService->start(); BLEAdvertising* pAdvertising = pServer->getAdvertising(); - pAdvertising->setServiceUUID(pService->getUUID().to128()); + pAdvertising->addServiceUUID(pService->getUUID()); + pAdvertising->addServiceUUID(BLEUUID((uint16_t)0x9876)); pAdvertising->start(); ESP_LOGD(LOG_TAG, "Advertising started!"); diff --git a/cpp_utils/tests/BLETests/main.cpp b/cpp_utils/tests/BLETests/main.cpp index 079cf3cf..732ae16d 100644 --- a/cpp_utils/tests/BLETests/main.cpp +++ b/cpp_utils/tests/BLETests/main.cpp @@ -7,30 +7,35 @@ extern "C" { // The list of sample entry points. -void SampleServer(void); +void Sample_MLE_15(void); void Sample1(void); +void SampleClient(void); +void SampleClient_Notify(void); +void SampleClientAndServer(void); +void SampleClientDisconnect(void); +void SampleClientWithWiFi(void); +void SampleNotify(void); void SampleRead(void); -void SampleWrite(void); void SampleScan(void); -void SampleNotify(void); -void SampleClient_Notify(void); -void SampleClient(void); -void Sample_MLE_15(void); void SampleSensorTag(void); - +void SampleServer(void); +void SampleWrite(void); // // Un-comment ONE of the following // --- void app_main(void) { - //SampleServer(); + //Sample_MLE_15(); //Sample1(); - //SampleRead(); - //SampleWrite(); - //SampleScan(); - //SampleNotify(); //SampleClient(); - SampleClient_Notify(); - //Sample_MLE_15(); + //SampleClient_Notify(); + //SampleClientAndServer(); + //SampleClientDisconnect(); + //SampleClientWithWiFi(); + //SampleNotify(); + //SampleRead(); //SampleSensorTag(); + //SampleScan(); + SampleServer(); + //SampleWrite(); } // app_main From 479a786e320c5c351dcf938f77bbb60494b105e7 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Fri, 13 Oct 2017 20:40:17 +0300 Subject: [PATCH 033/310] Fix Arduino Examples --- cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino | 2 +- cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino | 6 ++---- cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino | 2 +- cpp_utils/tests/BLETests/Arduino/BLE_write/BLE_write.ino | 7 ++----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino index 44506c26..8d329c20 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino @@ -53,7 +53,7 @@ void setup() { BLEDevice::init("MyESP32"); // Create the BLE Server - BLEServer *pServer = new BLEServer(); + BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino index 45ebf99f..38224a67 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino @@ -7,8 +7,6 @@ #include #include -BLEDevice ble; - // See the following for generating UUIDs: // https://www.uuidgenerator.net/ @@ -19,8 +17,8 @@ void setup() { Serial.begin(115200); Serial.println("Starting BLE work!"); - ble.init("MyESP32"); - BLEServer *pServer = new BLEServer(); + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino index a8ab2d7f..a348a666 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino @@ -70,7 +70,7 @@ void setup() { BLEDevice::init("UART Service"); // Create the BLE Server - BLEServer *pServer = new BLEServer(); + BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_write/BLE_write.ino b/cpp_utils/tests/BLETests/Arduino/BLE_write/BLE_write.ino index ed5ebc64..24a0cd23 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_write/BLE_write.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_write/BLE_write.ino @@ -7,8 +7,6 @@ #include #include -BLEDevice ble; - // See the following for generating UUIDs: // https://www.uuidgenerator.net/ @@ -41,9 +39,8 @@ void setup() { Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); Serial.println("5- See the magic =)"); - //ble.begin("MyESP32"); - ble.init("MyESP32"); - BLEServer *pServer = new BLEServer(); + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); From fdafe2f39462ddf256d48c00e257a1259ef5ae4c Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Sat, 14 Oct 2017 00:19:16 +0300 Subject: [PATCH 034/310] Enable Logging in Arduino IDE --- cpp_utils/BLEAddress.cpp | 3 +++ cpp_utils/BLEAdvertisedDevice.cpp | 3 +++ cpp_utils/BLEAdvertising.cpp | 3 +++ cpp_utils/BLECharacteristic.cpp | 3 +++ cpp_utils/BLECharacteristicCallbacks.cpp | 3 +++ cpp_utils/BLECharacteristicMap.cpp | 3 +++ cpp_utils/BLEClient.cpp | 3 +++ cpp_utils/BLEDescriptor.cpp | 3 +++ cpp_utils/BLEDescriptorMap.cpp | 3 +++ cpp_utils/BLEDevice.cpp | 3 +++ cpp_utils/BLERemoteCharacteristic.cpp | 3 +++ cpp_utils/BLERemoteDescriptor.cpp | 3 +++ cpp_utils/BLERemoteService.cpp | 3 +++ cpp_utils/BLEScan.cpp | 3 +++ cpp_utils/BLEServer.cpp | 3 +++ cpp_utils/BLEValue.cpp | 3 +++ 16 files changed, 48 insertions(+) diff --git a/cpp_utils/BLEAddress.cpp b/cpp_utils/BLEAddress.cpp index d2f7f8b0..895fedad 100644 --- a/cpp_utils/BLEAddress.cpp +++ b/cpp_utils/BLEAddress.cpp @@ -13,6 +13,9 @@ #include #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif /** diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 5074b811..83869cf6 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -17,6 +17,9 @@ #include #include "BLEAdvertisedDevice.h" #include "BLEUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG="BLEAdvertisedDevice"; BLEAdvertisedDevice::BLEAdvertisedDevice() { diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 26479663..e5179a41 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -12,6 +12,9 @@ #include #include "BLEUtils.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLEAdvertising"; diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 284032e7..18ba65dd 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -18,6 +18,9 @@ #include "BLEUtils.h" #include "BLE2902.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLECharacteristic"; diff --git a/cpp_utils/BLECharacteristicCallbacks.cpp b/cpp_utils/BLECharacteristicCallbacks.cpp index b7338659..46905b51 100644 --- a/cpp_utils/BLECharacteristicCallbacks.cpp +++ b/cpp_utils/BLECharacteristicCallbacks.cpp @@ -8,6 +8,9 @@ #if defined(CONFIG_BT_ENABLED) #include "BLECharacteristic.h" #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLECharacteristicCallbacks"; diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index 6ded0a63..bcf4a75d 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -9,6 +9,9 @@ #include #include #include "BLEService.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif /** diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 645c4aea..93f703a7 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -18,6 +18,9 @@ #include #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif /* * Design diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 4a7fda60..8362af0a 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -16,6 +16,9 @@ #include "BLEService.h" #include "BLEDescriptor.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLEDescriptor"; diff --git a/cpp_utils/BLEDescriptorMap.cpp b/cpp_utils/BLEDescriptorMap.cpp index b2116521..4e372e1d 100644 --- a/cpp_utils/BLEDescriptorMap.cpp +++ b/cpp_utils/BLEDescriptorMap.cpp @@ -11,6 +11,9 @@ #include "BLECharacteristic.h" #include "BLEDescriptor.h" #include // ESP32 BLE +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif /** * @brief Return the descriptor by UUID. diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index b2777e58..97d02b14 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -26,6 +26,9 @@ #include "BLEClient.h" #include "BLEUtils.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLEDevice"; diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 2e0fbb0e..ba781d29 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -18,6 +18,9 @@ #include "BLEUtils.h" #include "GeneralUtils.h" #include "BLERemoteDescriptor.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index a606929d..7a509f86 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -10,6 +10,9 @@ #include "BLERemoteDescriptor.h" #include "GeneralUtils.h" #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLERemoteDescriptor"; diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index c312e946..c0df06c3 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -13,6 +13,9 @@ #include "GeneralUtils.h" #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLERemoteService"; diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 925c09db..d3157b7f 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -17,6 +17,9 @@ #include "BLEScan.h" #include "BLEUtils.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLEScan"; diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 4a8dfd57..f7b44760 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -20,6 +20,9 @@ #include #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG = "BLEServer"; diff --git a/cpp_utils/BLEValue.cpp b/cpp_utils/BLEValue.cpp index d36d207a..49818e27 100644 --- a/cpp_utils/BLEValue.cpp +++ b/cpp_utils/BLEValue.cpp @@ -10,6 +10,9 @@ #include #include "BLEValue.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif static const char* LOG_TAG="BLEValue"; From e215aa9a9f07e2ef06f450054f69d3fffafa4965 Mon Sep 17 00:00:00 2001 From: kolban Date: Sun, 15 Oct 2017 12:42:38 -0500 Subject: [PATCH 035/310] Fixes for #121 --- cpp_utils/ArduinoBLE.md | 8 ++++++++ cpp_utils/Arduino_ESP32_BLE.library.properties | 2 +- cpp_utils/BLEServer.cpp | 1 - cpp_utils/BLEService.cpp | 7 ++++++- cpp_utils/BLEService.h | 6 ++++-- cpp_utils/FreeRTOS.cpp | 8 ++++++-- cpp_utils/FreeRTOS.h | 12 ++++++------ cpp_utils/Makefile.arduino | 6 ++++++ 8 files changed, 37 insertions(+), 13 deletions(-) diff --git a/cpp_utils/ArduinoBLE.md b/cpp_utils/ArduinoBLE.md index 07b37fd9..133292e8 100644 --- a/cpp_utils/ArduinoBLE.md +++ b/cpp_utils/ArduinoBLE.md @@ -23,6 +23,14 @@ And here you will find the `ESP32_BLE.zip` that is build from the latest source. ## Installing a new version If you have previously installed a version of the Arduino BLE Support and need to install a new one, follow the steps above to build yourself a new instance of the `ESP32_BLE.zip` that is ready for installation. I recommend removing the old one before installing the new one. To remove the old one, find the directory where the Arduino IDE stores your libraries (on Linux this is commonly `$HOME/Arduino`). In there you will find a directory called `libraries` and, if you have previously installed the BLE support, you will find a directory called `ESP32_BLE`. Remove that directory. +## Replacing the version that comes with Arduino-ESP32 +From October 2017 onwards, a build of the BLE libraries is supplied with the Arduino-ESP32 distribition which means that you should just be able to use the function without performing any additional steps. The intent is to keep the BLE libraries in this project completely in synch with the Arduino-ESP32 distribution. However, there may be times when a bug fix is needed which you may wish to try before an official distribution in Arduino-ESP32. That should be extremely rare. However, just in case it is needed, here is the recipe: + +1. Go to `/hardware/espressif/esp32/libraries` +2. Delete the directory called `BLE` +3. Go to your `/libraries` folder +4. Extract the `ESP32_BLE.zip` file there + ## Switching on debugging The BLE support contains extensive internal diagnostics which can be switched on by editing the file called `sdkconfig.h` and finding the lines which read: diff --git a/cpp_utils/Arduino_ESP32_BLE.library.properties b/cpp_utils/Arduino_ESP32_BLE.library.properties index b9f5972f..fdb01e2f 100644 --- a/cpp_utils/Arduino_ESP32_BLE.library.properties +++ b/cpp_utils/Arduino_ESP32_BLE.library.properties @@ -1,5 +1,5 @@ name=ESP32 BLE Arduino -version=0.4.2 +version=0.4.3 author=Neil Kolban maintainer=Neil Kolban sentence=BLE functions for ESP32 diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index f7b44760..0e66ec0e 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -201,7 +201,6 @@ void BLEServer::handleGATTServerEvent( // - uint16_t app_id case ESP_GATTS_REG_EVT: { m_gatts_if = gatts_if; - m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. break; } // ESP_GATTS_REG_EVT diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 9e7fa280..669ed261 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -56,9 +56,14 @@ BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { * @param [in] gatts_if The handle of the GATT server interface. * @return N/A. */ + void BLEService::executeCreate(BLEServer *pServer) { //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - getUUID(); // Needed for a weird bug fix + //getUUID(); // Needed for a weird bug fix + //char x[1000]; + //memcpy(x, &m_uuid, sizeof(m_uuid)); + //char x[10]; + //memcpy(x, &deleteMe, 10); m_pServer = pServer; m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 9a93aff7..19b472e9 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -78,10 +78,12 @@ class BLEService { uint16_t m_handle; BLECharacteristic* m_lastCreatedCharacteristic; BLEServer* m_pServer; + BLEUUID m_uuid; + char deleteMe[10]; //FreeRTOS::Semaphore m_serializeMutex; FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); - BLEUUID m_uuid; + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + uint32_t m_numHandles; uint16_t getHandle(); diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index dea73bfe..3cabd91b 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -4,8 +4,9 @@ * Created on: Feb 24, 2017 * Author: kolban */ -#include -#include +#include // Include the base FreeRTOS definitions +#include // Include the task definitions +#include // Include the semaphore definitions #include #include #include @@ -102,6 +103,9 @@ FreeRTOS::Semaphore::~Semaphore() { */ void FreeRTOS::Semaphore::give() { xSemaphoreGive(m_semaphore); +#ifdef ARDUINO_ARCH_ESP32 + FreeRTOS::sleep(10); +#endif ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); m_owner = ""; } // Semaphore::give diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index 320f4cc6..edaa9df4 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -10,9 +10,9 @@ #include #include -#include -#include -#include +#include // Include the base FreeRTOS definitions +#include // Include the task definitions +#include // Include the semaphore definitions /** @@ -40,9 +40,9 @@ class FreeRTOS { std::string toString(); private: SemaphoreHandle_t m_semaphore; - std::string m_name; - std::string m_owner; - uint32_t m_value; + std::string m_name; + std::string m_owner; + uint32_t m_value; }; }; diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index 609f8450..fc3ccd0f 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -51,6 +51,7 @@ BLE_FILES= \ GeneralUtils.h \ GeneralUtils.cpp +ARDUINO_LIBS=$(HOME)/Arduino/libraries build_ble: rm -rf Arduino/ESP32_BLE @@ -64,3 +65,8 @@ build_ble: rm -rf Arduino/ESP32_BLE @echo "---------------------------------------" @echo "ESP32_BLE.zip Arduino library now built" + +install: build_ble + rm -rf ${ARDUINO_LIBS}/ESP32_BLE + unzip Arduino/ESP32_BLE.zip -d $(ARDUINO_LIBS) + From 3893a18c2ad9d16173e784c9eab54939c14b435f Mon Sep 17 00:00:00 2001 From: kolban Date: Sun, 15 Oct 2017 22:30:02 -0500 Subject: [PATCH 036/310] Code changes for #127 --- cpp_utils/BLEClient.cpp | 101 ++++++++++++++++++++++++++++++++++++---- cpp_utils/BLEClient.h | 7 +++ cpp_utils/BLEDevice.cpp | 4 ++ cpp_utils/BLEServer.cpp | 2 +- 4 files changed, 104 insertions(+), 10 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 93f703a7..721e5efb 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -49,6 +49,7 @@ BLEClient::BLEClient() { m_conn_id = 0; m_gattc_if = 0; m_haveServices = false; + m_isConnected = false; // Initially, we are flagged as not connected. } // BLEClient @@ -76,7 +77,8 @@ bool BLEClient::connect(BLEAddress address) { // We need the connection handle that we get from registering the application. We register the app // and then block on its completion. When the event has arrived, we will have the handle. m_semaphoreRegEvt.take("connect"); - esp_err_t errRc = esp_ble_gattc_app_register(0); + + esp_err_t errRc = ::esp_ble_gattc_app_register(0); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; @@ -130,6 +132,22 @@ void BLEClient::gattClientEventHandler( // Execute handler code based on the type of event received. switch(event) { + + // + // ESP_GATTC_DISCONNECT_EVT + // + // disconnect: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + case ESP_GATTC_DISCONNECT_EVT: { + // If we receive a disconnect event, set the class flag that indicates that we are + // no longer connected. + m_isConnected = false; + break; + } // ESP_GATTC_DISCONNECT_EVT + + // // ESP_GATTC_OPEN_EVT // @@ -144,6 +162,7 @@ void BLEClient::gattClientEventHandler( if (m_pClientCallbacks != nullptr) { m_pClientCallbacks->onConnect(this); } + m_isConnected = true; // Flag us as connected. m_semaphoreOpenEvt.give(); break; } // ESP_GATTC_OPEN_EVT @@ -211,6 +230,17 @@ void BLEClient::gattClientEventHandler( } // gattClientEventHandler +uint16_t BLEClient::getConnId() { + return m_conn_id; +} // getConnId + + + +esp_gatt_if_t BLEClient::getGattcIf() { + return m_gattc_if; +} // getGattcIf + + /** * @brief Retrieve the address of the peer. * @@ -221,14 +251,29 @@ BLEAddress BLEClient::getPeerAddress() { } // getAddress -uint16_t BLEClient::getConnId() { - return m_conn_id; -} // getConnId - - -esp_gatt_if_t BLEClient::getGattcIf() { - return m_gattc_if; -} // getGattcIf +/** + * @brief Ask the BLE server for the RSSI value. + * @return The RSSI value. + */ +int BLEClient::getRssi() { + ESP_LOGD(LOG_TAG, ">> getRssi()"); + if (!isConnected()) { + ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected"); + return 0; + } + // We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive + // an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion. + // + m_semaphoreRssiCmplEvt.take("getRssi"); + esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative()); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc)); + return 0; + } + int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi"); + ESP_LOGD(LOG_TAG, "<< getRssi(): %d", rssiValue); + return rssiValue; +} // getRssi /** @@ -301,6 +346,44 @@ std::map* BLEClient::getServices() { return &m_servicesMap; } // getServices +/** + * @brief Handle a received GAP event. + * + * @param [in] event + * @param [in] param + */ +void BLEClient::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + ESP_LOGD(LOG_TAG, "BLEClient ... handling GAP event!"); + switch(event) { + // + // 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: { + m_semaphoreRssiCmplEvt.give((uint32_t)param->read_rssi_cmpl.rssi); + break; + } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + + default: + break; + } +} // handleGAPEvent + + +/** + * @brief Are we connected to a partner? + * @return True if we are connected and false if we are not connected. + */ +bool BLEClient::isConnected() { + return m_isConnected; +} // isConnected + /** * @brief Set the callbacks that will be invoked. diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 494f51dd..25704a34 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -32,9 +32,14 @@ class BLEClient { bool connect(BLEAddress address); void disconnect(); BLEAddress getPeerAddress(); + int getRssi(); std::map* getServices(); BLERemoteService* getService(const char* uuid); BLERemoteService* getService(BLEUUID uuid); + void handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + bool isConnected(); void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); std::string toString(); @@ -55,11 +60,13 @@ class BLEClient { uint16_t m_conn_id; // int m_deviceType; esp_gatt_if_t m_gattc_if; + bool m_isConnected; BLEClientCallbacks* m_pClientCallbacks; FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); + FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; bool m_haveServices; // Have we previously obtain the set of services. }; // class BLEDevice diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 97d02b14..e58cac7f 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -147,6 +147,10 @@ void BLEDevice::gapEventHandler( BLEDevice::m_pServer->handleGAPEvent(event, param); } + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->handleGAPEvent(event, param); + } + if (BLEDevice::m_pScan != nullptr) { BLEDevice::getScan()->gapEventHandler(event, param); } diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 0e66ec0e..0d9bd6b5 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -123,7 +123,7 @@ uint16_t BLEServer::getGattsIf() { } /** - * @brief Handle a receiver GAP event. + * @brief Handle a received GAP event. * * @param [in] event * @param [in] param From b40811ffa2843dc37e1ead9b46083a8f482fcb52 Mon Sep 17 00:00:00 2001 From: kolban Date: Tue, 17 Oct 2017 23:37:48 -0500 Subject: [PATCH 037/310] Changes for #132 --- networking/bootwifi/BootWiFi.cpp | 16 ++++++++++++++-- networking/bootwifi/BootWiFi.h | 9 ++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index ab9f4f3d..a6f8ebb6 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -222,7 +222,7 @@ void BootWiFi::bootWiFi2() { m_wifi.setWifiEventHandler(new BootWifiEventHandler(this)); if (checkOverrideGpio()) { ESP_LOGD(LOG_TAG, "- GPIO override detected"); - m_wifi.startAP("Duktape", "Duktape"); + m_wifi.startAP(m_ssid, m_password); } else { // There was NO GPIO override, proceed as normal. This means we retrieve // our stored access point information of the access point we should connect @@ -249,19 +249,30 @@ void BootWiFi::bootWiFi2() { // point that serves up a web server and allow a browser user to specify // the details that will be eventually used to allow us to connect // as a station. - m_wifi.startAP("Duktape", "Duktape"); + m_wifi.startAP(m_ssid, m_password); } // We do NOT have connection info } ESP_LOGD(LOG_TAG, "<< bootWiFi2"); } // bootWiFi2 +/** + * @brief Set the userid/password pair that will be used for the ESP32 access point. + * @param [in] ssid The network id of the ESP32 when it becomes an access point. + * @param [in] password The password for the ESP32 when it becomes an access point. + */ +void BootWiFi::setAccessPointCredentials(std::string ssid, std::string password) { + m_ssid = ssid; + m_password = password; +} // setAccessPointCredentials + void BootWiFi::boot() { ESP_LOGD(LOG_TAG, ">> boot"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " | BootWiFi |"); ESP_LOGD(LOG_TAG, " +----------+"); + ESP_LOGD(LOG_TAG, " Access point credentials: %s/%s", m_ssid.c_str(), m_password.c_str()); m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. bootWiFi2(); m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. @@ -270,4 +281,5 @@ void BootWiFi::boot() { BootWiFi::BootWiFi() { m_httpServerStarted = false; + setAccessPointCredentials("esp32", "password"); } diff --git a/networking/bootwifi/BootWiFi.h b/networking/bootwifi/BootWiFi.h index e2b26f94..3d37dccd 100644 --- a/networking/bootwifi/BootWiFi.h +++ b/networking/bootwifi/BootWiFi.h @@ -19,13 +19,16 @@ class BootWiFi { private: friend BootWifiEventHandler; void bootWiFi2(); - WiFi m_wifi; - HttpServer m_httpServer; - bool m_httpServerStarted; + WiFi m_wifi; + HttpServer m_httpServer; + bool m_httpServerStarted; + std::string m_ssid; + std::string m_password; FreeRTOS::Semaphore m_completeSemaphore = FreeRTOS::Semaphore("completeSemaphore"); public: BootWiFi(); + void setAccessPointCredentials(std::string ssid, std::string password); void boot(); }; From f9a876e19c5ac2e1e7d4160639abd85e84906cee Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 19 Oct 2017 03:22:10 +0200 Subject: [PATCH 038/310] Optimization in display function to speed up drawing --- .../Adafruit_SSD1306.cpp | 60 ++++++++++++------- .../Adafruit_SSD1306.h | 10 +++- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.cpp b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.cpp index 12a7404a..daa4ec22 100644 --- a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.cpp +++ b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.cpp @@ -195,7 +195,7 @@ void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) { dev_config.duty_cycle_pos = 0; dev_config.cs_ena_posttrans = 0; dev_config.cs_ena_pretrans = 0; - dev_config.clock_speed_hz = 100000; // 100KHz + dev_config.clock_speed_hz = 1000000; // 1MHz dev_config.spics_io_num = cs; dev_config.flags = 0; dev_config.queue_size = 1; @@ -393,27 +393,33 @@ void Adafruit_SSD1306::dim(boolean dim) { } void Adafruit_SSD1306::display(void) { - ssd1306_command(SSD1306_COLUMNADDR); - ssd1306_command(0); // Column start address (0 = reset) - ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) - - ssd1306_command(SSD1306_PAGEADDR); - ssd1306_command(0); // Page start address (0 = reset) - #if SSD1306_LCDHEIGHT == 64 - ssd1306_command(7); // Page end address - #endif - #if SSD1306_LCDHEIGHT == 32 - ssd1306_command(3); // Page end address - #endif - #if SSD1306_LCDHEIGHT == 16 - ssd1306_command(1); // Page end address - #endif + + gpio_set_level((gpio_num_t)dc, 0); + uint8_t cmd_buffer[] = {SSD1306_COLUMNADDR, 0, SSD1306_LCDWIDTH-1, SSD1306_PAGEADDR, 0, (SSD1306_LCDHEIGHT/8-1)}; + spi_transaction_t trans_desc; + trans_desc.addr= 0; + trans_desc.cmd = 0; + trans_desc.flags = 0; + trans_desc.length = 6 * 8; + trans_desc.rxlength = 0; + trans_desc.tx_buffer = cmd_buffer; + trans_desc.rx_buffer = NULL; + + ESP_ERROR_CHECK(spi_device_transmit(spi_handle, &trans_desc)); gpio_set_level((gpio_num_t)dc, 1); - for (uint16_t i=0; i<(SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT/8); i++) { - fastSPIwrite(buffer[i]); - } + //spi_transaction_t trans_desc; + trans_desc.addr= 0; + trans_desc.cmd = 0; + trans_desc.flags = 0; + trans_desc.length = SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH; + trans_desc.rxlength = 0; + trans_desc.tx_buffer = buffer; + trans_desc.rx_buffer = NULL; + + ESP_ERROR_CHECK(spi_device_transmit(spi_handle, &trans_desc)); + } // clear everything @@ -424,8 +430,8 @@ void Adafruit_SSD1306::clearDisplay(void) { inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) { spi_transaction_t trans_desc; - trans_desc.address = 0; - trans_desc.command = 0; + trans_desc.addr= 0; + trans_desc.cmd = 0; trans_desc.flags = 0; trans_desc.length = 8; trans_desc.rxlength = 0; @@ -651,3 +657,15 @@ void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h } } } +#ifndef ARDUINO +void Adafruit_SSD1306::println(char* text){ + print(text); + print((char*)"\n"); +} +void Adafruit_SSD1306::print(char* text){ + while(*text != 0) { + write(*text); + text++; + } +} +#endif diff --git a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h index e25c03f8..8a12aafb 100644 --- a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h +++ b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h @@ -44,8 +44,8 @@ All text above, and the splash screen must be included in any redistribution SSD1306_96_16 -----------------------------------------------------------------------*/ -// #define SSD1306_128_64 - #define SSD1306_128_32 + #define SSD1306_128_64 +// #define SSD1306_128_32 // #define SSD1306_96_16 /*=========================================================================*/ @@ -123,7 +123,11 @@ class Adafruit_SSD1306 : public Adafruit_GFX { void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS, bool reset=true); void ssd1306_command(uint8_t c); - + void ssd1306_command(uint8_t *c, uint8_t l); +#ifndef ARDUINO + void print(char*); + void println(char*); +#endif void clearDisplay(void); void invertDisplay(uint8_t i); void display(); From 0acf6a3d566ad72a6f85425b41db43c7762170f4 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 19 Oct 2017 03:41:06 +0200 Subject: [PATCH 039/310] Optimization in display function to speed up drawing, added esp-idf example --- .../Adafruit_SSD1306.h | 2 +- .../examples/main/component.mk | 8 + .../examples/main/main.cpp | 13 + .../examples/main/tests.cpp | 349 ++++++++++++++++++ 4 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 hardware/displays/Adafruit_SSD1306-Library/examples/main/component.mk create mode 100644 hardware/displays/Adafruit_SSD1306-Library/examples/main/main.cpp create mode 100644 hardware/displays/Adafruit_SSD1306-Library/examples/main/tests.cpp diff --git a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h index 8a12aafb..98ef0227 100644 --- a/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h +++ b/hardware/displays/Adafruit_SSD1306-Library/Adafruit_SSD1306.h @@ -123,7 +123,7 @@ class Adafruit_SSD1306 : public Adafruit_GFX { void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS, bool reset=true); void ssd1306_command(uint8_t c); - void ssd1306_command(uint8_t *c, uint8_t l); + #ifndef ARDUINO void print(char*); void println(char*); diff --git a/hardware/displays/Adafruit_SSD1306-Library/examples/main/component.mk b/hardware/displays/Adafruit_SSD1306-Library/examples/main/component.mk new file mode 100644 index 00000000..61f8990c --- /dev/null +++ b/hardware/displays/Adafruit_SSD1306-Library/examples/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/hardware/displays/Adafruit_SSD1306-Library/examples/main/main.cpp b/hardware/displays/Adafruit_SSD1306-Library/examples/main/main.cpp new file mode 100644 index 00000000..bc0bd5aa --- /dev/null +++ b/hardware/displays/Adafruit_SSD1306-Library/examples/main/main.cpp @@ -0,0 +1,13 @@ +#include "freertos/FreeRTOS.h" +#include "esp_event.h" + +extern "C"{ + void app_main(void); +} +void test_task(void*); + +void app_main(void) +{ + xTaskCreate(&test_task, "test", 2048, NULL, 5, NULL); +} + diff --git a/hardware/displays/Adafruit_SSD1306-Library/examples/main/tests.cpp b/hardware/displays/Adafruit_SSD1306-Library/examples/main/tests.cpp new file mode 100644 index 00000000..c93fc522 --- /dev/null +++ b/hardware/displays/Adafruit_SSD1306-Library/examples/main/tests.cpp @@ -0,0 +1,349 @@ +/* + * tests.cpp + * + * Created on: Oct 9, 2017 + * Author: chegewara + */ +#define enablePartialUpdate + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +//#include "math.h" +#include "sdkconfig.h" + +#include "Adafruit_SSD1306.h" +#define SCLK_PIN GPIO_NUM_18 +#define DIN_PIN GPIO_NUM_23 +#define DC_PIN GPIO_NUM_16 +#define CS_PIN GPIO_NUM_5 +#define RST_PIN GPIO_NUM_14 + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define NUMFLAKES 10 +#define XPOS 0 +#define YPOS 1 +#define DELTAY 2 +#define delay(x) vTaskDelay(x/portTICK_PERIOD_MS) + +#define LOGO16_GLCD_HEIGHT 16 +#define LOGO16_GLCD_WIDTH 16 + +static const unsigned char logo16_glcd_bmp[] = +{ 0b00000000, 0b11000000, + 0b00000001, 0b11000000, + 0b00000001, 0b11000000, + 0b00000011, 0b11100000, + 0b11110011, 0b11100000, + 0b11111110, 0b11111000, + 0b01111110, 0b11111111, + 0b00110011, 0b10011111, + 0b00011111, 0b11111100, + 0b00001101, 0b01110000, + 0b00011011, 0b10100000, + 0b00111111, 0b11100000, + 0b00111111, 0b11110000, + 0b01111100, 0b11110000, + 0b01110000, 0b01110000, + 0b00000000, 0b00110000 }; + +Adafruit_SSD1306 display = Adafruit_SSD1306(DIN_PIN, SCLK_PIN, DC_PIN, RST_PIN, CS_PIN); + +void testdrawchar(void) { + display.setTextSize(1); + display.setTextColor(WHITE); + display.setCursor(0,0); + + for (uint8_t i=0; i < 168; i++) { + if (i == '\n') continue; + display.write(i); + //if ((i > 0) && (i % 14 == 0)) + //display.println(); + } + display.display(); +} + +void testdrawcircle(void) { + for (int16_t i=0; i0; i-=5) { + display.fillTriangle(display.width()/2, display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, color); + if (color == WHITE) color = BLACK; + else color = WHITE; + display.display(); + } +} + +void testdrawroundrect(void) { + for (int16_t i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + } + delay(25); + + display.clearDisplay(); + for (int16_t i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + } + for (int16_t i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + } + delay(25); + + display.clearDisplay(); + for (int16_t i=0; i display.height()) { + icons[f][XPOS] = rand()%(display.width()); + icons[f][YPOS] = 0; + icons[f][DELTAY] = rand()%(5) + 1; + } + } + } +} + +void test_task(void*) { + while(1){ + display.begin(); + //display.setContrast(50); + display.display(); + + delay(2000); + display.clearDisplay(); // clears the screen and buffer + + // draw a single pixel + display.drawPixel(10, 10, WHITE); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw many lines + testdrawline(); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw rectangles + testdrawrect(); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw multiple rectangles + testfillrect(); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw mulitple circles + testdrawcircle(); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw a circle, 10 pixel radius + display.fillCircle(display.width()/2, display.height()/2, 10, WHITE); + display.display(); + delay(2000); + display.clearDisplay(); + + testdrawroundrect(); + delay(2000); + display.clearDisplay(); + + testfillroundrect(); + delay(2000); + display.clearDisplay(); + + testdrawtriangle(); + delay(2000); + display.clearDisplay(); + + testfilltriangle(); + delay(2000); + display.clearDisplay(); + + // draw the first ~12 characters in the font + testdrawchar(); + display.display(); + delay(2000); + display.clearDisplay(); + + // draw scrolling text + testscrolltext(); + delay(2000); + display.clearDisplay(); + + // text display tests + display.setTextSize(2); + display.setTextColor(WHITE); + display.setCursor(0,0); + display.println((char*)"Hello, world!"); + display.setTextColor(WHITE, BLACK); // 'inverted' text + display.println((char*)"3.141592"); + display.setTextSize(1); + display.setTextColor(WHITE); + display.print((char*)"0x"); + display.println((char*)"DEADBEEF"); + display.display(); + delay(2000); + + // rotation example + display.clearDisplay(); + display.setRotation(1); // rotate 90 degrees counter clockwise, can also use values of 2 and 3 to go further. + display.setTextSize(1); + display.setTextColor(WHITE); + display.setCursor(0,0); + display.println((char*)"Rotation"); + display.setTextSize(1); + display.println((char*)"Example!"); + display.display(); + delay(2000); + + // revert back to no rotation + display.setRotation(0); + + // miniature bitmap display + display.clearDisplay(); + display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, WHITE); + display.display(); + + // invert the display + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + // draw a bitmap icon and 'animate' movement + testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_WIDTH, LOGO16_GLCD_HEIGHT); + } +} + + From 7aaa5b4d8088f98163d6bb386a6c075c3a0a191b Mon Sep 17 00:00:00 2001 From: kolban Date: Sun, 22 Oct 2017 11:09:53 -0500 Subject: [PATCH 040/310] Work on #134 --- cpp_utils/Apa102.cpp | 46 +++++++++ cpp_utils/Apa102.h | 24 +++++ cpp_utils/SPI.cpp | 21 ++-- cpp_utils/SmartLED.cpp | 218 +++++++++++++++++++++++++++++++++++++++++ cpp_utils/SmartLED.h | 53 ++++++++++ cpp_utils/WS2812.cpp | 6 +- 6 files changed, 355 insertions(+), 13 deletions(-) create mode 100644 cpp_utils/Apa102.cpp create mode 100644 cpp_utils/Apa102.h create mode 100644 cpp_utils/SmartLED.cpp create mode 100644 cpp_utils/SmartLED.h diff --git a/cpp_utils/Apa102.cpp b/cpp_utils/Apa102.cpp new file mode 100644 index 00000000..274ec83f --- /dev/null +++ b/cpp_utils/Apa102.cpp @@ -0,0 +1,46 @@ +/* + * Apa102.cpp + * + * Created on: Oct 22, 2017 + * Author: kolban + */ + +#include "Apa102.h" + +Apa102::Apa102() { +} + +Apa102::~Apa102() { + +} + + +void Apa102::init() { + mySPI.init(); +} + +/** + * @brief Show the pixels on an APA102 device. + * The pixels that have been set are pushed to the APA102 devices. + */ +void Apa102::show() { + // We follow the data sheet for the APA102. To signify a new stream of data + // we send 32bits of 0 value. Following that are 4 bytes of color data. The + // data is 0b111 nnnnn where `nnnnn` is the brightness of the pixels. + // The following data is 8 bits for blue, 8 bits for green and 8 bits for red. + + // Send APA102 start. + mySPI.transferByte(0x0); + mySPI.transferByte(0x0); + mySPI.transferByte(0x0); + mySPI.transferByte(0x0); + + double brigthnessScale = getBrightness() / 100.0; + // Loop over all the pixels in the pixels array to set the colors. + for (int i=0; i 0); #ifdef DEBUG for (auto i=0; i %2d %.2x", i, data[i]); + ESP_LOGD(LOG_TAG, "> %2d %.2x", i, data[i]); } #endif spi_transaction_t trans_desc; @@ -125,7 +126,7 @@ void SPI::transfer(uint8_t* data, size_t dataLen) { //ESP_LOGI(tag, "... Transferring"); esp_err_t rc = ::spi_device_transmit(m_handle, &trans_desc); if (rc != ESP_OK) { - ESP_LOGE(tag, "transfer:spi_device_transmit: %d", rc); + ESP_LOGE(LOG_TAG, "transfer:spi_device_transmit: %d", rc); } } // transmit diff --git a/cpp_utils/SmartLED.cpp b/cpp_utils/SmartLED.cpp new file mode 100644 index 00000000..e0267585 --- /dev/null +++ b/cpp_utils/SmartLED.cpp @@ -0,0 +1,218 @@ +/* + * SmartLED.cpp + * + * Created on: Oct 22, 2017 + * Author: kolban + */ + +#include "SmartLED.h" +#include "string.h" +#include + +const char* LOG_TAG = "SmartLED"; + +SmartLED::SmartLED() { + m_brightness = 100; + m_pixelCount = 0; + m_pixels = nullptr; + m_colorOrder = (char *)"GRB"; +} // SmartLED + + +SmartLED::~SmartLED() { + if (m_pixels != nullptr) { + delete[] m_pixels; // Delete the allocated storage for the pixels. + } +} // ~SmartLED + +/** + * @brief Clear all the pixel colors. + * + * This sets all the pixels to off which is no brightness for all of the color channels. + * The LEDs are not actually updated until a call to show() is subsequently made. + */ +void SmartLED::clear() { + for (auto i=0; im_pixelCount; i++) { + m_pixels[i].red = 0; + m_pixels[i].green = 0; + m_pixels[i].blue = 0; + } // End loop over all the pixel +} // clear + + +/** + * @brief Get the brightness as a percentage. + * @return The brightness as a percentage. + */ +uint32_t SmartLED::getBrightness() { + return m_brightness; +} // getBrightness + +/** + * @brief Return the number of pixels in the chain. + * @return The number of pixels in the chain as previously set by setPixelCount(). + */ +uint16_t SmartLED::getPixelCount() { + return m_pixelCount; +} // getPixelCount + + +/** + * @brief Set the brightness as a percentage. + * @param [in] percent The brightness. + */ +void SmartLED::setBrightness(uint32_t percent) { + m_brightness = percent; +} // setBrightness + +/** + * @brief Set the color order of data sent to the LEDs. + * + * Data is sent to the WS2812s in a serial fashion. There are 8 bits of data for each of the three + * channel colors (red, green and blue). The WS2812 LEDs typically expect the data to arrive in the + * order of "green" then "red" then "blue". However, this has been found to vary between some + * models and manufacturers. What this means is that some want "red", "green", "blue" and still others + * have their own orders. This function can be called to override the default ordering of "GRB". + * We can specify + * an alternate order by supply an alternate three character string made up of 'R', 'G' and 'B' + * for example "RGB". + */ +void SmartLED::setColorOrder(char *colorOrder) { + if (colorOrder != nullptr && strlen(colorOrder) == 3) { + m_colorOrder = colorOrder; + } +} // setColorOrder + + +/** + * @brief Set the given pixel to the specified color. + * + * The LEDs are not actually updated until a call to show() is subsequently made. + * + * @param [in] index The pixel that is to have its color set. + * @param [in] red The amount of red in the pixel. + * @param [in] green The amount of green in the pixel. + * @param [in] blue The amount of blue in the pixel. + */ +void SmartLED::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { + //assert(index < m_pixelCount); + + m_pixels[index].red = red; + m_pixels[index].green = green; + m_pixels[index].blue = blue; +} // setPixel + + +/** + * @brief Set the given pixel to the specified color. + * + * The LEDs are not actually updated until a call to show(). + * + * @param [in] index The pixel that is to have its color set. + * @param [in] pixel The color value of the pixel. + */ +void SmartLED::setPixel(uint16_t index, pixel_t pixel) { + //assert(index < m_pixelCount); + m_pixels[index] = pixel; +} // setPixel + + +/** + * @brief Set the given pixel to the specified color. + * + * The LEDs are not actually updated until a call to show(). + * + * @param [in] index The pixel that is to have its color set. + * @param [in] pixel The color value of the pixel. + */ +void SmartLED::setPixel(uint16_t index, uint32_t pixel) { + //assert(index < m_pixelCount); + + m_pixels[index].red = pixel & 0xff; + m_pixels[index].green = (pixel & 0xff00) >> 8; + m_pixels[index].blue = (pixel & 0xff0000) >> 16; +} // setPixel + +void SmartLED::setPixelCount(uint16_t pixelCount) { + ESP_LOGD(LOG_TAG, ">> setPixelCount: %d", pixelCount); + if (m_pixels != nullptr) { + delete[] m_pixels; + } + m_pixelCount = pixelCount; + m_pixels = new pixel_t[pixelCount]; // Allocate the storage for the pixels. + ESP_LOGD(LOG_TAG, "<< setPixelCount"); +} + +/** + * @brief Set the given pixel to the specified HSB color. + * + * The LEDs are not actually updated until a call to show(). + * + * @param [in] index The pixel that is to have its color set. + * @param [in] hue The amount of hue in the pixel (0-360). + * @param [in] saturation The amount of saturation in the pixel (0-255). + * @param [in] brightness The amount of brightness in the pixel (0-255). + */ +void SmartLED::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { + double sat_red; + double sat_green; + double sat_blue; + double ctmp_red; + double ctmp_green; + double ctmp_blue; + double new_red; + double new_green; + double new_blue; + double dSaturation=(double)saturation/255; + double dBrightness=(double)brightness/255; + + //assert(index < pixelCount); + + if (hue < 120) { + sat_red = (120 - hue) / 60.0; + sat_green = hue / 60.0; + sat_blue = 0; + } else if (hue < 240) { + sat_red = 0; + sat_green = (240 - hue) / 60.0; + sat_blue = (hue - 120) / 60.0; + } else { + sat_red = (hue - 240) / 60.0; + sat_green = 0; + sat_blue = (360 - hue) / 60.0; + } + + if (sat_red>1.0) { + sat_red = 1.0; + } + if (sat_green>1.0) { + sat_green = 1.0; + } + if (sat_blue>1.0) { + sat_blue = 1.0; + } + + ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); + ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); + ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); + + if (dBrightness < 0.5) { + new_red = dBrightness * ctmp_red; + new_green = dBrightness * ctmp_green; + new_blue = dBrightness * ctmp_blue; + } else { + new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; + new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; + new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; + } + + m_pixels[index].red = (uint8_t)(new_red*255); + m_pixels[index].green = (uint8_t)(new_green*255); + m_pixels[index].blue = (uint8_t)(new_blue*255); +} // setHSBPixel + + + + + + diff --git a/cpp_utils/SmartLED.h b/cpp_utils/SmartLED.h new file mode 100644 index 00000000..e70c4843 --- /dev/null +++ b/cpp_utils/SmartLED.h @@ -0,0 +1,53 @@ +/* + * SmartLED.h + * + * Created on: Oct 22, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_SMARTLED_H_ +#define COMPONENTS_SMARTLED_H_ +#include +/** + * @brief A data type representing the color of a pixel. + */ +typedef struct { + /** + * @brief The red component of the pixel. + */ + uint8_t red; + /** + * @brief The green component of the pixel. + */ + uint8_t green; + /** + * @brief The blue component of the pixel. + */ + uint8_t blue; +} pixel_t; + + +class SmartLED { +public: + SmartLED(); + virtual ~SmartLED(); + uint32_t getBrightness(); + uint16_t getPixelCount(); + virtual void init() = 0; + virtual void show() = 0; + void setBrightness(uint32_t percent); + void setColorOrder(char *order); + void setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue); + void setPixel(uint16_t index, pixel_t pixel); + void setPixel(uint16_t index, uint32_t pixel); + void setPixelCount(uint16_t pixelCount); + void setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness); + void clear(); +protected: + uint32_t m_brightness; + char* m_colorOrder; + uint16_t m_pixelCount; + pixel_t* m_pixels; +}; + +#endif /* COMPONENTS_SMARTLED_H_ */ diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index 64f3c825..ca0a5b24 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -10,7 +10,7 @@ #include "sdkconfig.h" #include "WS2812.h" -static char tag[] = "WS2812"; +static const char* LOG_TAG = "WS2812"; /** * A NeoPixel is defined by 3 bytes ... red, green and blue. @@ -80,7 +80,7 @@ static uint8_t getChannelValueByType(char type, pixel_t pixel) { case 'G': return pixel.green; } - ESP_LOGW(tag, "Unknown color channel 0x%2x", type); + ESP_LOGW(LOG_TAG, "Unknown color channel 0x%2x", type); return 0; } // getChannelValueByType @@ -153,7 +153,7 @@ void WS2812::show() { (getChannelValueByType(this->colorOrder[1], this->pixels[i]) << 8) | (getChannelValueByType(this->colorOrder[2], this->pixels[i])); - ESP_LOGD(tag, "Pixel value: %x", currentPixel); + ESP_LOGD(LOG_TAG, "Pixel value: %x", currentPixel); for (int j=23; j>=0; j--) { // We have 24 bits of data representing the red, green amd blue channels. The value of the // 24 bits to output is in the variable current_pixel. We now need to stream this value From b59dccb1acdd1a9c5fa2e9e8445e2adf2d9b2482 Mon Sep 17 00:00:00 2001 From: kolban Date: Sun, 22 Oct 2017 11:27:48 -0500 Subject: [PATCH 041/310] Updated docs for #130 --- Documentation/C++ HTTP Server.pdf | Bin 81135 -> 89383 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Documentation/C++ HTTP Server.pdf b/Documentation/C++ HTTP Server.pdf index 1f68419d3ab54c42bf3110cd81405fd5f85fc044..ee5ab10423eca002de7d1c4d194108fc109bd64e 100755 GIT binary patch delta 71502 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 From a86e1c89a9c72885171ba6b406934ff3a04bde3b Mon Sep 17 00:00:00 2001 From: Ilian Georgiev Date: Wed, 25 Oct 2017 19:11:04 +0300 Subject: [PATCH 042/310] Remove an unneeded inclusion --- cpp_utils/WiFi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 78571eba..cd614acd 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -25,7 +25,6 @@ #include #include -#include static const char* LOG_TAG = "WiFi"; From ad30b7317c86f916ed0d07f9aeea98b77cb208fc Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 25 Oct 2017 22:40:17 -0500 Subject: [PATCH 043/310] sync --- cpp_utils/FreeRTOS.cpp | 61 +++++++++++++++++++++++++++------ cpp_utils/FreeRTOS.h | 3 ++ cpp_utils/HttpServer.cpp | 2 +- cpp_utils/Memory.cpp | 18 ++++++++++ cpp_utils/Memory.h | 17 +++++++++ cpp_utils/SockServ.cpp | 2 +- cpp_utils/WiFi.cpp | 74 +++++++++++++++++++++++----------------- 7 files changed, 133 insertions(+), 44 deletions(-) create mode 100644 cpp_utils/Memory.cpp create mode 100644 cpp_utils/Memory.h diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 3cabd91b..4097237a 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -77,23 +77,44 @@ uint32_t FreeRTOS::getTimeSinceStart() { */ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); - xSemaphoreTake(m_semaphore, portMAX_DELAY); + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + xSemaphoreTake(m_semaphore, portMAX_DELAY); + } m_owner = owner; - xSemaphoreGive(m_semaphore); + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + ESP_LOGV(LOG_TAG, "Semaphore released: %s", toString().c_str()); m_owner = ""; return m_value; } // wait + FreeRTOS::Semaphore::Semaphore(std::string name) { - m_semaphore = xSemaphoreCreateMutex(); + m_usePthreads = true; + if (m_usePthreads) { + pthread_mutex_init(&m_pthread_mutex, nullptr); + } else { + m_semaphore = xSemaphoreCreateMutex(); + } + m_name = name; m_owner = ""; m_value = 0; } + FreeRTOS::Semaphore::~Semaphore() { - vSemaphoreDelete(m_semaphore); + if (m_usePthreads) { + pthread_mutex_destroy(&m_pthread_mutex); + } else { + vSemaphoreDelete(m_semaphore); + } } @@ -102,7 +123,11 @@ FreeRTOS::Semaphore::~Semaphore() { * The Semaphore is given. */ void FreeRTOS::Semaphore::give() { - xSemaphoreGive(m_semaphore); + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } #ifdef ARDUINO_ARCH_ESP32 FreeRTOS::sleep(10); #endif @@ -119,7 +144,7 @@ void FreeRTOS::Semaphore::give() { void FreeRTOS::Semaphore::give(uint32_t value) { m_value = value; give(); -} +} // give /** @@ -127,7 +152,11 @@ void FreeRTOS::Semaphore::give(uint32_t value) { */ void FreeRTOS::Semaphore::giveFromISR() { BaseType_t higherPriorityTaskWoken; - xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); + if (m_usePthreads) { + assert(false); + } else { + xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); + } } // giveFromISR @@ -138,7 +167,11 @@ void FreeRTOS::Semaphore::giveFromISR() { void FreeRTOS::Semaphore::take(std::string owner) { ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); - xSemaphoreTake(m_semaphore, portMAX_DELAY); + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + xSemaphoreTake(m_semaphore, portMAX_DELAY); + } m_owner = owner; ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take @@ -152,17 +185,23 @@ void FreeRTOS::Semaphore::take(std::string owner) void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); m_owner = owner; - xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + if (m_usePthreads) { + assert(false); + } else { + xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + } ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take + std::string FreeRTOS::Semaphore::toString() { std::stringstream stringStream; stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; return stringStream.str(); -} +} // toString + void FreeRTOS::Semaphore::setName(std::string name) { m_name = name; -} +} // setName diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index edaa9df4..fe318601 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -9,6 +9,7 @@ #define MAIN_FREERTOS_H_ #include #include +#include #include // Include the base FreeRTOS definitions #include // Include the task definitions @@ -40,9 +41,11 @@ class FreeRTOS { std::string toString(); private: SemaphoreHandle_t m_semaphore; + pthread_mutex_t m_pthread_mutex; std::string m_name; std::string m_owner; uint32_t m_value; + bool m_usePthreads; }; }; diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 168941ad..7b081297 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -84,7 +84,7 @@ HttpServer::~HttpServer() { */ class HttpServerTask: public Task { public: - HttpServerTask(std::string name): Task(name, 16*1024) { + HttpServerTask(std::string name): Task(name, 12*1024) { m_pHttpServer = nullptr; }; diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp new file mode 100644 index 00000000..7180c648 --- /dev/null +++ b/cpp_utils/Memory.cpp @@ -0,0 +1,18 @@ +/* + * Memory.cpp + * + * Created on: Oct 24, 2017 + * Author: kolban + */ + +#include "Memory.h" + +Memory::Memory() { + // TODO Auto-generated constructor stub + +} + +Memory::~Memory() { + // TODO Auto-generated destructor stub +} + diff --git a/cpp_utils/Memory.h b/cpp_utils/Memory.h new file mode 100644 index 00000000..59510356 --- /dev/null +++ b/cpp_utils/Memory.h @@ -0,0 +1,17 @@ +/* + * Memory.h + * + * Created on: Oct 24, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_MEMORY_H_ +#define COMPONENTS_CPP_UTILS_MEMORY_H_ + +class Memory { +public: + Memory(); + virtual ~Memory(); +}; + +#endif /* COMPONENTS_CPP_UTILS_MEMORY_H_ */ diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 7c7bcf99..55416823 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -172,7 +172,7 @@ void SockServ::start() { //m_serverSocket.setSSL(m_useSSL); m_serverSocket.listen(m_port); // Create a socket and start listening on it. ESP_LOGD(LOG_TAG, "Now listening on port %d", m_port); - FreeRTOS::startTask(acceptTask, "acceptTask", this, 16*1024); + FreeRTOS::startTask(acceptTask, "acceptTask", this, 8*1024); } // start diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 78571eba..1d275453 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -343,37 +343,49 @@ std::string WiFi::getStaSSID() { * @return A vector of WiFiAPRecord instances. */ std::vector WiFi::scan() { - ::nvs_flash_init(); - ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK( esp_wifi_start() ); - wifi_scan_config_t conf; - memset(&conf, 0, sizeof(conf)); - conf.show_hidden = true; - esp_err_t rc = ::esp_wifi_scan_start(&conf, true); - if (rc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); - } - uint16_t apCount; - rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); - wifi_ap_record_t *list = - (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list)); - std::vector apRecords; - for (auto i=0; i> scan"); + std::vector apRecords; + + ::nvs_flash_init(); + ::tcpip_adapter_init(); + + ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK( esp_wifi_start() ); + + wifi_scan_config_t conf; + memset(&conf, 0, sizeof(conf)); + conf.show_hidden = true; + + esp_err_t rc = ::esp_wifi_scan_start(&conf, true); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); + return apRecords; + } + + uint16_t apCount; // Number of access points available. + rc = ::esp_wifi_scan_get_ap_num(&apCount); + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + + wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + if (list == nullptr) { + ESP_LOGE(LOG_TAG, "Failed to allocate memory"); + return apRecords; + } + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list)); + + for (auto i=0; i Date: Wed, 25 Oct 2017 22:53:32 -0500 Subject: [PATCH 044/310] Changes for #138 --- cpp_utils/WiFi.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 124ca73e..2332e97f 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "sdkconfig.h" @@ -379,11 +380,15 @@ std::vector WiFi::scan() { for (auto i=0; i rhs.m_rssi;}); return apRecords; } // scan From 8d98d9418ed854973176a6a4ce3e43a7f5d5e6c4 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 28 Oct 2017 10:19:18 +0200 Subject: [PATCH 045/310] Refactoring BLEAdvertisedDevice + update examples --- cpp_utils/BLEAdvertisedDevice.cpp | 39 +++++++++++-------- cpp_utils/BLEAdvertisedDevice.h | 3 +- cpp_utils/tests/BLETests/SampleClient.cpp | 2 +- .../tests/BLETests/SampleClient_Notify.cpp | 2 +- cpp_utils/tests/BLETests/SampleServer.cpp | 9 ++--- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 83869cf6..a332c7e2 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -108,10 +108,21 @@ BLEScan* BLEAdvertisedDevice::getScan() { * @brief Get the Service UUID. * @return The Service UUID of the advertised device. */ -BLEUUID BLEAdvertisedDevice::getServiceUUID() { - return m_serviceUUID; +BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful + return m_serviceUUIDs[0]; } // getServiceUUID +/** + * @brief Check advertised serviced for existence required UUID + * @return Return true if service is advertised + */ +bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){ + for (int i = 0; i < m_serviceUUIDs.size(); ++i) { + if(m_serviceUUIDs[i].equals(uuid)) + return true; + } + return false; +} /** * @brief Get the TX Power. @@ -231,23 +242,19 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } // ESP_BLE_AD_TYPE_FLAG - case ESP_BLE_AD_TYPE_16SRV_CMPL: { // Adv Data Type: 0x03 - setServiceUUID(BLEUUID(*reinterpret_cast(payload))); - break; - } // ESP_BLE_AD_TYPE_16SRV_CMPL - + case ESP_BLE_AD_TYPE_16SRV_CMPL: case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 - setServiceUUID(BLEUUID(*reinterpret_cast(payload))); + for (int var = 0; var < length/2; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*2))); + } break; } // ESP_BLE_AD_TYPE_16SRV_PART - case ESP_BLE_AD_TYPE_32SRV_CMPL: { // Adv Data Type: 0x05 - setServiceUUID(BLEUUID(*reinterpret_cast(payload))); - break; - } // ESP_BLE_AD_TYPE_32SRV_CMPL - + case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 - setServiceUUID(BLEUUID(*reinterpret_cast(payload))); + for (int var = 0; var < length/4; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4))); + } break; } // ESP_BLE_AD_TYPE_32SRV_PART @@ -368,9 +375,9 @@ void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { * @param [in] serviceUUID The discovered serviceUUID */ void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) { - m_serviceUUID = serviceUUID; + m_serviceUUIDs.push_back(serviceUUID); m_haveServiceUUID = true; - ESP_LOGD(LOG_TAG, "- setServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); + ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); } // setRSSI diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index fbdeeec7..aea6da08 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -38,6 +38,7 @@ class BLEAdvertisedDevice { BLEUUID getServiceUUID(); int8_t getTXPower(); + bool isAdvertisingService(BLEUUID uuid); bool haveAppearance(); bool haveManufacturerData(); bool haveName(); @@ -79,7 +80,7 @@ class BLEAdvertisedDevice { std::string m_name; BLEScan* m_pScan; int m_rssi; - BLEUUID m_serviceUUID; + std::vector m_serviceUUIDs; int8_t m_txPower; }; diff --git a/cpp_utils/tests/BLETests/SampleClient.cpp b/cpp_utils/tests/BLETests/SampleClient.cpp index c91942db..3b1c7572 100644 --- a/cpp_utils/tests/BLETests/SampleClient.cpp +++ b/cpp_utils/tests/BLETests/SampleClient.cpp @@ -88,7 +88,7 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); - if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { advertisedDevice.getScan()->stop(); ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); diff --git a/cpp_utils/tests/BLETests/SampleClient_Notify.cpp b/cpp_utils/tests/BLETests/SampleClient_Notify.cpp index 5ebdbf47..aaecbbf7 100644 --- a/cpp_utils/tests/BLETests/SampleClient_Notify.cpp +++ b/cpp_utils/tests/BLETests/SampleClient_Notify.cpp @@ -83,7 +83,7 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); - if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { advertisedDevice.getScan()->stop(); ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); diff --git a/cpp_utils/tests/BLETests/SampleServer.cpp b/cpp_utils/tests/BLETests/SampleServer.cpp index c7dffd1a..70268f46 100644 --- a/cpp_utils/tests/BLETests/SampleServer.cpp +++ b/cpp_utils/tests/BLETests/SampleServer.cpp @@ -18,13 +18,13 @@ class MainBLEServer: public Task { void run(void *data) { ESP_LOGD(LOG_TAG, "Starting BLE work!"); - BLEDevice::init("MYDEVICE"); + BLEDevice::init("ESP32"); BLEServer* pServer = BLEDevice::createServer(); - BLEService* pService = pServer->createService(BLEUUID((uint16_t)0x1234)); + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); BLECharacteristic* pCharacteristic = pService->createCharacteristic( - BLEUUID((uint16_t)0x99AA), + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_INDICATE @@ -39,8 +39,7 @@ class MainBLEServer: public Task { pService->start(); BLEAdvertising* pAdvertising = pServer->getAdvertising(); - pAdvertising->addServiceUUID(pService->getUUID()); - pAdvertising->addServiceUUID(BLEUUID((uint16_t)0x9876)); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); pAdvertising->start(); ESP_LOGD(LOG_TAG, "Advertising started!"); From e00bf587f40c792167757f5357721b58ac354bcf Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 28 Oct 2017 11:30:36 -0500 Subject: [PATCH 046/310] fixes for #143 --- cpp_utils/WiFi.cpp | 161 ++++++++++++++++++++++++++++++++++++++------- cpp_utils/WiFi.h | 40 +++++------ 2 files changed, 156 insertions(+), 45 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 2332e97f..077a629e 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "GeneralUtils.h" #include #include #include @@ -164,24 +165,49 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool if (m_eventLoopStarted) { esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. } else { - ESP_ERROR_CHECK(esp_event_loop_init(WiFi::eventHandler, this)); // Initialze the event handler. + esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } m_eventLoopStarted = true; } //ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); - ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); + esp_err_t errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } wifi_config_t sta_config; ::memset(&sta_config, 0, sizeof(sta_config)); ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); ::memcpy(sta_config.sta.password, password.data(), password.size()); sta_config.sta.bssid_set = 0; - ESP_ERROR_CHECK(::esp_wifi_set_config(WIFI_IF_STA, &sta_config)); - ESP_ERROR_CHECK(::esp_wifi_start()); + errRc = ::esp_wifi_set_config(WIFI_IF_STA, &sta_config); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } m_gotIpEvt.take("connectAP"); - ESP_ERROR_CHECK(::esp_wifi_connect()); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } m_gotIpEvt.wait("connectAP"); ESP_LOGD(LOG_TAG, "<< connectAP"); } // connectAP @@ -349,12 +375,41 @@ std::vector WiFi::scan() { ::nvs_flash_init(); ::tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); + // If we have already started the event loop, then change the handler otherwise + // start the event loop. + if (m_eventLoopStarted) { + esp_event_loop_set_cb(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + } + else { + esp_err_t errRc = ::esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + m_eventLoopStarted = true; + } + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK(::esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(::esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK( esp_wifi_start() ); + esp_err_t errRc = ::esp_wifi_init(&cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } wifi_scan_config_t conf; memset(&conf, 0, sizeof(conf)); @@ -375,7 +430,11 @@ std::vector WiFi::scan() { ESP_LOGE(LOG_TAG, "Failed to allocate memory"); return apRecords; } - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list)); + errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } for (auto i=0; igetEventHandler(), m_pWifiEventHandler); } else { - ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); + esp_err_t errRc = ::esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } m_eventLoopStarted = true; } wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) ); + esp_err_t errRc = ::esp_wifi_init(&cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } wifi_config_t apConfig; ::memset(&apConfig, 0, sizeof(apConfig)); ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); @@ -429,8 +504,16 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { apConfig.ap.ssid_hidden = 0; apConfig.ap.max_connection = 4; apConfig.ap.beacon_interval = 100; - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &apConfig) ); - ESP_ERROR_CHECK( esp_wifi_start() ); + errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } ESP_LOGD(LOG_TAG, "<< startAP"); } // startAP @@ -547,7 +630,11 @@ std::string WiFiAPRecord::toString() { } // toString MDNS::MDNS() { - ESP_ERROR_CHECK(mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server)); + esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } MDNS::~MDNS() { @@ -615,22 +702,38 @@ void MDNS::setInstance(const std::string& instance) { * @return N/A. */ void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { - ESP_ERROR_CHECK(mdns_service_add(m_mdns_server, service, proto, port)); + esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_add: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // serviceAdd void MDNS::serviceInstanceSet(const char* service, const char* proto, const char* instance) { - ESP_ERROR_CHECK(mdns_service_instance_set(m_mdns_server, service, proto, instance)); + esp_err_t errRc = ::mdns_service_instance_set(m_mdns_server, service, proto, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_instance_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // serviceInstanceSet void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) { - ESP_ERROR_CHECK(mdns_service_port_set(m_mdns_server, service, proto, port)); + esp_err_t errRc = ::mdns_service_port_set(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_port_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // servicePortSet void MDNS::serviceRemove(const char* service, const char* proto) { - ESP_ERROR_CHECK(mdns_service_remove(m_mdns_server, service, proto)); + esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // serviceRemove @@ -641,7 +744,11 @@ void MDNS::serviceRemove(const char* service, const char* proto) { * @return N/A. */ void MDNS::setHostname(const char* hostname) { - ESP_ERROR_CHECK(mdns_set_hostname(m_mdns_server,hostname)); + esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setHostname @@ -652,5 +759,9 @@ void MDNS::setHostname(const char* hostname) { * @return N/A. */ void MDNS::setInstance(const char* instance) { - ESP_ERROR_CHECK(mdns_set_instance(m_mdns_server, instance)); + esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setInstance diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index d087c032..87fd79b5 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -116,29 +116,29 @@ class WiFi { public: WiFi(); ~WiFi(); - void addDNSServer(const std::string& ip); - void addDNSServer(const char* ip); - void addDNSServer(ip_addr_t ip); - void setDNSServer(int numdns, const std::string& ip); - void setDNSServer(int numdns, const char* ip); - void setDNSServer(int numdns, ip_addr_t ip); - struct in_addr getHostByName(const std::string& hostName); - struct in_addr getHostByName(const char* hostName); - void connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); - void dump(); - static std::string getApMac(); + void addDNSServer(const std::string& ip); + void addDNSServer(const char* ip); + void addDNSServer(ip_addr_t ip); + void setDNSServer(int numdns, const std::string& ip); + void setDNSServer(int numdns, const char* ip); + void setDNSServer(int numdns, ip_addr_t ip); + struct in_addr getHostByName(const std::string& hostName); + struct in_addr getHostByName(const char* hostName); + void connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); + void dump(); + static std::string getApMac(); static tcpip_adapter_ip_info_t getApIpInfo(); - static std::string getApSSID(); - static std::string getMode(); + static std::string getApSSID(); + static std::string getMode(); static tcpip_adapter_ip_info_t getStaIpInfo(); - static std::string getStaMac(); - static std::string getStaSSID(); + static std::string getStaMac(); + static std::string getStaSSID(); std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd); - void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); - void setIPInfo(const char* ip, const char* gw, const char* netmask); - void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); + void startAP(const std::string& ssid, const std::string& passwd); + void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); + void setIPInfo(const char* ip, const char* gw, const char* netmask); + void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); + void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); }; #endif /* MAIN_WIFI_H_ */ From 27501f2a70181b382641bbb1cfa20c8d9b58a818 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 29 Oct 2017 12:22:22 -0500 Subject: [PATCH 047/310] Fixes for #149 --- cpp_utils/JSON.cpp | 14 ++++++++++++-- cpp_utils/JSON.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index f7b296f8..6fa866b5 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -195,10 +195,13 @@ std::size_t JsonArray::size() { return cJSON_GetArraySize(m_node); } // size - +/** + * @brief Constructor + */ JsonObject::JsonObject(cJSON* node) { m_node = node; -} +} // JsonObject + /** * @brief Get the named boolean value from the object. @@ -268,6 +271,13 @@ bool JsonObject::hasItem(std::string name) { } // hasItem +/** + * @brief Determine if this represents a valid JSON node. + * @return True if this is a valid node and false otherwise. + */ +bool JsonObject::isValid() { + return m_node != nullptr; +} // isValid /** * @brief Set the named array property. diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index bca2d647..b41a22fb 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -65,6 +65,7 @@ class JsonObject { JsonObject getObject(std::string name); std::string getString(std::string name); bool hasItem(std::string name); + bool isValid(); void setArray(std::string name, JsonArray array); void setBoolean(std::string name, bool value); void setDouble(std::string name, double value); From 36d7e221d114df2c37b611580dcd42da3e676a98 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 29 Oct 2017 12:58:29 -0500 Subject: [PATCH 048/310] Fixes for #143 --- cpp_utils/WiFi.cpp | 304 +++++++++++++++++++---------------- cpp_utils/WiFi.h | 3 +- cpp_utils/WiFiEventHandler.h | 61 ++++--- 3 files changed, 193 insertions(+), 175 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 077a629e..7958f0ba 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -43,8 +43,6 @@ static void setDNSServer(char *ip) { */ - - /** * @brief Creates and uses a default event handler */ @@ -69,6 +67,7 @@ WiFi::~WiFi() { delete m_pWifiEventHandler; } + /** * @brief Add a reference to a DNS server. * @@ -87,22 +86,26 @@ WiFi::~WiFi() { * @return N/A. */ void WiFi::addDNSServer(const std::string& ip) { - addDNSServer(ip.c_str()); + addDNSServer(ip.c_str()); } // addDNSServer + void WiFi::addDNSServer(const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) - addDNSServer(ip); + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + addDNSServer(ip); + } } // addDNSServer + void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - ::dns_setserver(m_dnsCount, &ip); - m_dnsCount++; - m_dnsCount %= 2; + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ::dns_setserver(m_dnsCount, &ip); + m_dnsCount++; + m_dnsCount %= 2; } // addDNSServer + /** * @brief Set a reference to a DNS server. * @@ -120,20 +123,24 @@ void WiFi::addDNSServer(ip_addr_t ip) { * @return N/A. */ void WiFi::setDNSServer(int numdns, const std::string& ip) { - setDNSServer(numdns, ip.c_str()); + setDNSServer(numdns, ip.c_str()); } // setDNSServer + void WiFi::setDNSServer(int numdns, const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) - setDNSServer(numdns, dns_server); + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + setDNSServer(numdns, dns_server); + } } // setDNSServer + void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - ::dns_setserver(numdns, &ip); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ::dns_setserver(numdns, &ip); } // setDNSServer + /** * @brief Connect to an external access point. * @@ -158,8 +165,6 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); } - - // If the event loop has already started then change the callback else // start the event loop. if (m_eventLoopStarted) { @@ -172,9 +177,8 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool } m_eventLoopStarted = true; } + // Now, one way or another, the event handler is WiFi::eventHandler. - - //ESP_ERROR_CHECK(esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler)); esp_err_t errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -212,25 +216,34 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool ESP_LOGD(LOG_TAG, "<< connectAP"); } // connectAP + /** * @brief Dump diagnostics to the log. */ void WiFi::dump() { - ESP_LOGD(LOG_TAG, "WiFi Dump"); - ESP_LOGD(LOG_TAG, "---------"); - char ipAddrStr[30]; - ip_addr_t ip = ::dns_getserver(0); - inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); - ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); + ESP_LOGD(LOG_TAG, "WiFi Dump"); + ESP_LOGD(LOG_TAG, "---------"); + char ipAddrStr[30]; + ip_addr_t ip = ::dns_getserver(0); + inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); + ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); } // dump /** * @brief Primary event handler interface. */ -esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { - WiFi *pWiFi = (WiFi *)ctx; +/* STATIC */ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { + // This is the common event handler that we have provided for all event processing. It is called for every event + // that is received by the WiFi subsystem. The "ctx" parameter is an instance of the current WiFi object that we are + // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this + // an indirection vector to the real caller. + + WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. + + // Invoke the event handler. esp_err_t rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + // If the event we received indicates that we now have an IP address then unlock the mutex that // indicates we are waiting for an IP. if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { @@ -245,23 +258,22 @@ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { * @return The AP IP Info. */ tcpip_adapter_ip_info_t WiFi::getApIpInfo() { - tcpip_adapter_ip_info_t ipInfo; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); - return ipInfo; + tcpip_adapter_ip_info_t ipInfo; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); + return ipInfo; } // getApIpInfo - /** * @brief Get the MAC address of the AP interface. * @return The MAC address of the AP interface. */ std::string WiFi::getApMac() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_AP, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_AP, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); } // getApMac @@ -270,9 +282,9 @@ std::string WiFi::getApMac() { * @return The AP SSID. */ std::string WiFi::getApSSID() { - wifi_config_t conf; - esp_wifi_get_config(WIFI_IF_AP, &conf); - return std::string((char *)conf.sta.ssid); + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_AP, &conf); + return std::string((char *)conf.sta.ssid); } // getApSSID @@ -284,20 +296,21 @@ std::string WiFi::getApSSID() { * @return The IP address of the host or 0.0.0.0 if not found. */ struct in_addr WiFi::getHostByName(const std::string& hostName) { - return getHostByName(hostName.c_str()); + return getHostByName(hostName.c_str()); } // getHostByName + struct in_addr WiFi::getHostByName(const char* hostName) { - struct in_addr retAddr; - struct hostent *he = gethostbyname(hostName); - if (he == nullptr) { - retAddr.s_addr = 0; - ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); - } else { - retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); - } - return retAddr; + struct in_addr retAddr; + struct hostent *he = gethostbyname(hostName); + if (he == nullptr) { + retAddr.s_addr = 0; + ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); + } else { + retAddr = *(struct in_addr *)(he->h_addr_list[0]); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + } + return retAddr; } // getHostByName @@ -306,20 +319,20 @@ struct in_addr WiFi::getHostByName(const char* hostName) { * @return The WiFi Mode. */ std::string WiFi::getMode() { - wifi_mode_t mode; - esp_wifi_get_mode(&mode); - switch(mode) { - case WIFI_MODE_NULL: - return "WIFI_MODE_NULL"; - case WIFI_MODE_STA: - return "WIFI_MODE_STA"; - case WIFI_MODE_AP: - return "WIFI_MODE_AP"; - case WIFI_MODE_APSTA: - return "WIFI_MODE_APSTA"; - default: - return "unknown"; - } + wifi_mode_t mode; + esp_wifi_get_mode(&mode); + switch(mode) { + case WIFI_MODE_NULL: + return "WIFI_MODE_NULL"; + case WIFI_MODE_STA: + return "WIFI_MODE_STA"; + case WIFI_MODE_AP: + return "WIFI_MODE_AP"; + case WIFI_MODE_APSTA: + return "WIFI_MODE_APSTA"; + default: + return "unknown"; + } } // getMode @@ -328,9 +341,9 @@ std::string WiFi::getMode() { * @return The STA IP Info. */ tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { - tcpip_adapter_ip_info_t ipInfo; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - return ipInfo; + tcpip_adapter_ip_info_t ipInfo; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + return ipInfo; } // getStaIpInfo @@ -339,11 +352,11 @@ tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { * @return The MAC address of the STA interface. */ std::string WiFi::getStaMac() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); } // getStaMac @@ -352,9 +365,9 @@ std::string WiFi::getStaMac() { * @return The STA SSID. */ std::string WiFi::getStaSSID() { - wifi_config_t conf; - esp_wifi_get_config(WIFI_IF_STA, &conf); - return std::string((char *)conf.ap.ssid); + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + return std::string((char *)conf.ap.ssid); } // getStaSSID @@ -378,16 +391,16 @@ std::vector WiFi::scan() { // If we have already started the event loop, then change the handler otherwise // start the event loop. if (m_eventLoopStarted) { - esp_event_loop_set_cb(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); - } - else { - esp_err_t errRc = ::esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. + } else { + esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } m_eventLoopStarted = true; } + // Now, one way or another, the event handler is WiFi::eventHandler. wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_err_t errRc = ::esp_wifi_init(&cfg); @@ -395,16 +408,19 @@ std::vector WiFi::scan() { ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + errRc = ::esp_wifi_start(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -430,6 +446,7 @@ std::vector WiFi::scan() { ESP_LOGE(LOG_TAG, "Failed to allocate memory"); return apRecords; } + errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -437,12 +454,12 @@ std::vector WiFi::scan() { } for (auto i=0; igetEventHandler(), m_pWifiEventHandler); - } - else { - esp_err_t errRc = ::esp_event_loop_init(m_pWifiEventHandler->getEventHandler(), m_pWifiEventHandler); + esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. + } else { + esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } m_eventLoopStarted = true; } + // Now, one way or another, the event handler is WiFi::eventHandler. + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_err_t errRc = ::esp_wifi_init(&cfg); @@ -547,25 +566,25 @@ void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { * @return N/A. */ void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask) { - setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); + setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); } // setIPInfo void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { - uint32_t new_ip; - uint32_t new_gw; - uint32_t new_netmask; + uint32_t new_ip; + uint32_t new_gw; + uint32_t new_netmask; - auto success = (bool)inet_pton(AF_INET, ip, &new_ip); - success = success && inet_pton(AF_INET, gw, &new_gw); - success = success && inet_pton(AF_INET, netmask, &new_netmask); + auto success = (bool)inet_pton(AF_INET, ip, &new_ip); + success = success && inet_pton(AF_INET, gw, &new_gw); + success = success && inet_pton(AF_INET, netmask, &new_netmask); - if(!success) { - return; - } + if(!success) { + return; + } - setIPInfo(new_ip, new_gw, new_netmask); + setIPInfo(new_ip, new_gw, new_netmask); } // setIPInfo @@ -576,22 +595,21 @@ void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { * @param [in] netmask Our TCP/IP netmask value. */ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { - this->ip = ip; - this->gw = gw; - this->netmask = netmask; - - if(ip != 0 && gw != 0 && netmask != 0) { - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; - - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } else { - ip = 0; - ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); - } + this->ip = ip; + this->gw = gw; + this->netmask = netmask; + + if(ip != 0 && gw != 0 && netmask != 0) { + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } else { + ip = 0; + ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); + } } // setIPInfo @@ -601,23 +619,23 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { * @return A string representation of the WiFi access point record. */ std::string WiFiAPRecord::toString() { - std::string auth; - switch(getAuthMode()) { - case WIFI_AUTH_OPEN: - auth = "WIFI_AUTH_OPEN"; - break; - case WIFI_AUTH_WEP: - auth = "WIFI_AUTH_WEP"; - break; - case WIFI_AUTH_WPA_PSK: - auth = "WIFI_AUTH_WPA_PSK"; - break; - case WIFI_AUTH_WPA2_PSK: - auth = "WIFI_AUTH_WPA2_PSK"; - break; - case WIFI_AUTH_WPA_WPA2_PSK: - auth = "WIFI_AUTH_WPA_WPA2_PSK"; - break; + std::string auth; + switch(getAuthMode()) { + case WIFI_AUTH_OPEN: + auth = "WIFI_AUTH_OPEN"; + break; + case WIFI_AUTH_WEP: + auth = "WIFI_AUTH_WEP"; + break; + case WIFI_AUTH_WPA_PSK: + auth = "WIFI_AUTH_WPA_PSK"; + break; + case WIFI_AUTH_WPA2_PSK: + auth = "WIFI_AUTH_WPA2_PSK"; + break; + case WIFI_AUTH_WPA_WPA2_PSK: + auth = "WIFI_AUTH_WPA_WPA2_PSK"; + break; default: auth = ""; break; @@ -630,18 +648,18 @@ std::string WiFiAPRecord::toString() { } // toString MDNS::MDNS() { - esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } MDNS::~MDNS() { - if (m_mdns_server != nullptr) { - mdns_free(m_mdns_server); - } - m_mdns_server = nullptr; + if (m_mdns_server != nullptr) { + mdns_free(m_mdns_server); + } + m_mdns_server = nullptr; } /** diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 87fd79b5..37584534 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "FreeRTOS.h" #include "WiFiEventHandler.h" @@ -104,7 +105,7 @@ class WiFiAPRecord { */ class WiFi { private: - static esp_err_t eventHandler(void* ctx, system_event_t* event); + static esp_err_t eventHandler(void* ctx, system_event_t* event); uint32_t ip; uint32_t gw; uint32_t netmask; diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index 0791089a..612dc8f3 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -79,40 +79,39 @@ */ class WiFiEventHandler { public: - WiFiEventHandler(); - virtual ~WiFiEventHandler(); - system_event_cb_t getEventHandler(); - virtual esp_err_t apStaConnected(); - virtual esp_err_t apStaDisconnected(); - virtual esp_err_t apStart(); - virtual esp_err_t apStop(); - virtual esp_err_t staConnected(); - virtual esp_err_t staDisconnected(); - virtual esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip); - virtual esp_err_t staStart(); - virtual esp_err_t staStop(); - virtual esp_err_t wifiReady(); + WiFiEventHandler(); + virtual ~WiFiEventHandler(); + virtual esp_err_t apStaConnected(); + virtual esp_err_t apStaDisconnected(); + virtual esp_err_t apStart(); + virtual esp_err_t apStop(); + system_event_cb_t getEventHandler(); + virtual esp_err_t staConnected(); + virtual esp_err_t staDisconnected(); + virtual esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip); + virtual esp_err_t staStart(); + virtual esp_err_t staStop(); + virtual esp_err_t wifiReady(); - /** - * Get the next WiFi event handler in the chain, if there is one. - * @return The next WiFi event handler in the chain or nullptr if there is none. - */ - WiFiEventHandler *getNextHandler() { - return m_nextHandler; - } - - /** - * Set the next WiFi event handler in the chain. - * @param [in] nextHandler The next WiFi event handler in the chain. - */ - void setNextHandler(WiFiEventHandler* nextHandler) { - this->m_nextHandler = nextHandler; - } + /** + * Get the next WiFi event handler in the chain, if there is one. + * @return The next WiFi event handler in the chain or nullptr if there is none. + */ + WiFiEventHandler *getNextHandler() { + return m_nextHandler; + } + /** + * Set the next WiFi event handler in the chain. + * @param [in] nextHandler The next WiFi event handler in the chain. + */ + void setNextHandler(WiFiEventHandler* nextHandler) { + this->m_nextHandler = nextHandler; + } private: - friend class WiFi; - WiFiEventHandler *m_nextHandler; - static esp_err_t eventHandler(void* ctx, system_event_t* event); + friend class WiFi; + WiFiEventHandler *m_nextHandler; + static esp_err_t eventHandler(void* ctx, system_event_t* event); }; #endif /* MAIN_WIFIEVENTHANDLER_H_ */ From 6153b3208e19facfee089a592d84d27f9a19853e Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 29 Oct 2017 17:26:27 -0500 Subject: [PATCH 049/310] Changes for #140 --- cpp_utils/WiFi.cpp | 134 +++++++++++++++++++-------------------------- cpp_utils/WiFi.h | 2 + 2 files changed, 57 insertions(+), 79 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 7958f0ba..bb5c5069 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -53,10 +53,7 @@ WiFi::WiFi() , m_pWifiEventHandler(nullptr) { m_eventLoopStarted = false; - ::nvs_flash_init(); - wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); - esp_wifi_init(&config); - ::tcpip_adapter_init(); + m_initCalled = false; m_pWifiEventHandler = new WiFiEventHandler(); } // WiFi @@ -100,6 +97,7 @@ void WiFi::addDNSServer(const char* ip) { void WiFi::addDNSServer(ip_addr_t ip) { ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); ::dns_setserver(m_dnsCount, &ip); m_dnsCount++; m_dnsCount %= 2; @@ -137,6 +135,7 @@ void WiFi::setDNSServer(int numdns, const char* ip) { void WiFi::setDNSServer(int numdns, ip_addr_t ip) { ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); ::dns_setserver(numdns, &ip); } // setDNSServer @@ -154,6 +153,8 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { void WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ ESP_LOGD(LOG_TAG, ">> connectAP"); + init(); + if (ip != 0 && gw != 0 && netmask != 0) { ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client @@ -165,27 +166,7 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); } - // If the event loop has already started then change the callback else - // start the event loop. - if (m_eventLoopStarted) { - esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. - } else { - esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - m_eventLoopStarted = true; - } - // Now, one way or another, the event handler is WiFi::eventHandler. - - esp_err_t errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); @@ -217,6 +198,7 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool } // connectAP + /** * @brief Dump diagnostics to the log. */ @@ -258,6 +240,7 @@ void WiFi::dump() { * @return The AP IP Info. */ tcpip_adapter_ip_info_t WiFi::getApIpInfo() { + //init(); tcpip_adapter_ip_info_t ipInfo; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); return ipInfo; @@ -270,6 +253,7 @@ tcpip_adapter_ip_info_t WiFi::getApIpInfo() { */ std::string WiFi::getApMac() { uint8_t mac[6]; + //init(); esp_wifi_get_mac(WIFI_IF_AP, mac); auto mac_str = (char*) malloc(18); sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -283,6 +267,7 @@ std::string WiFi::getApMac() { */ std::string WiFi::getApSSID() { wifi_config_t conf; + //init(); esp_wifi_get_config(WIFI_IF_AP, &conf); return std::string((char *)conf.sta.ssid); } // getApSSID @@ -372,21 +357,9 @@ std::string WiFi::getStaSSID() { /** - * @brief Perform a WiFi scan looking for access points. - * - * An access point scan is performed and a vector of WiFi access point records - * is built and returned with one record per found scan instance. The scan is - * performed in a blocking fashion and will not return until the set of scanned - * access points has been built. - * - * @return A vector of WiFiAPRecord instances. + * @brief Initialize WiFi. */ -std::vector WiFi::scan() { - ESP_LOGD(LOG_TAG, ">> scan"); - std::vector apRecords; - - ::nvs_flash_init(); - ::tcpip_adapter_init(); +/* PRIVATE */ void WiFi::init() { // If we have already started the event loop, then change the handler otherwise // start the event loop. @@ -402,20 +375,44 @@ std::vector WiFi::scan() { } // Now, one way or another, the event handler is WiFi::eventHandler. - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_err_t errRc = ::esp_wifi_init(&cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + if (!m_initCalled) { + ::nvs_flash_init(); + ::tcpip_adapter_init(); - errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t errRc = ::esp_wifi_init(&cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } + m_initCalled = true; +} // init + - errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); +/** + * @brief Perform a WiFi scan looking for access points. + * + * An access point scan is performed and a vector of WiFi access point records + * is built and returned with one record per found scan instance. The scan is + * performed in a blocking fashion and will not return until the set of scanned + * access points has been built. + * + * @return A vector of WiFiAPRecord instances. + */ +std::vector WiFi::scan() { + ESP_LOGD(LOG_TAG, ">> scan"); + std::vector apRecords; + + init(); + + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); @@ -478,41 +475,16 @@ std::vector WiFi::scan() { */ void WiFi::startAP(const std::string& ssid, const std::string& password) { ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); - ::nvs_flash_init(); - ::tcpip_adapter_init(); - - // If we have already started the event loop, then change the handler otherwise - // start the event loop. - - if (m_eventLoopStarted) { - esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. - } else { - esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - m_eventLoopStarted = true; - } - // Now, one way or another, the event handler is WiFi::eventHandler. + init(); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_err_t errRc = ::esp_wifi_init(&cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + + // Build the apConfig structure. wifi_config_t apConfig; ::memset(&apConfig, 0, sizeof(apConfig)); ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); @@ -523,11 +495,13 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { apConfig.ap.ssid_hidden = 0; apConfig.ap.max_connection = 4; apConfig.ap.beacon_interval = 100; + errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + errRc = ::esp_wifi_start(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -595,6 +569,8 @@ void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { * @param [in] netmask Our TCP/IP netmask value. */ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { + init(); + this->ip = ip; this->gw = gw; this->netmask = netmask; diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 37584534..0a980af0 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -106,12 +106,14 @@ class WiFiAPRecord { class WiFi { private: static esp_err_t eventHandler(void* ctx, system_event_t* event); + void init(); uint32_t ip; uint32_t gw; uint32_t netmask; WiFiEventHandler* m_pWifiEventHandler; uint8_t m_dnsCount=0; bool m_eventLoopStarted; + bool m_initCalled; FreeRTOS::Semaphore m_gotIpEvt = FreeRTOS::Semaphore("GotIpEvt"); public: From b600ac4b2af01b3476479f4149934c3c2676138a Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 30 Oct 2017 19:32:31 -0500 Subject: [PATCH 050/310] #152 --- cpp_utils/GeneralUtils.cpp | 12 ++--- cpp_utils/GeneralUtils.h | 16 +++---- cpp_utils/HttpServer.cpp | 69 +++++++++++++++++++++++++--- cpp_utils/HttpServer.h | 19 +++++++- cpp_utils/Memory.cpp | 92 +++++++++++++++++++++++++++++++++++--- cpp_utils/Memory.h | 22 +++++++-- cpp_utils/WiFi.cpp | 2 +- 7 files changed, 197 insertions(+), 35 deletions(-) diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 6bfde84f..e8eb3a0a 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -24,15 +24,6 @@ static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; -GeneralUtils::GeneralUtils() { - // TODO Auto-generated constructor stub - -} - -GeneralUtils::~GeneralUtils() { - // TODO Auto-generated destructor stub -} - static int base64EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; } // base64EncodedLength @@ -272,6 +263,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { } */ + /** * @brief Dump a representation of binary data to the console. * @@ -315,6 +307,7 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { } } // hexDump + /** * @brief Convert an IP address to string. * @param ip The 4 byte IP address. @@ -404,6 +397,7 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "Unknown ESP_ERR error"; } // errorToString + /** * @brief Restart the ESP32. */ diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index ce75e43b..2d55abfc 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -16,15 +16,13 @@ */ class GeneralUtils { public: - GeneralUtils(); - virtual ~GeneralUtils(); - static bool base64Encode(const std::string &in, std::string *out); - static bool base64Decode(const std::string &in, std::string *out); - static bool endsWith(std::string str, char c); - static const char *errorToString(esp_err_t errCode); - static void hexDump(const uint8_t *pData, uint32_t length); - static std::string ipToString(uint8_t *ip); - static void restart(); + static bool base64Decode(const std::string& in, std::string* out); + static bool base64Encode(const std::string& in, std::string* out); + static bool endsWith(std::string str, char c); + static const char* errorToString(esp_err_t errCode); + static void hexDump(const uint8_t* pData, uint32_t length); + static std::string ipToString(uint8_t* ip); + static void restart(); }; #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 7b081297..5578993f 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -12,6 +12,7 @@ #include #include "HttpRequest.h" #include "HttpResponse.h" +#include "Memory.h" #include "FileSystem.h" #include "WebSocket.h" #include "GeneralUtils.h" @@ -232,7 +233,7 @@ class HttpServerTask: public Task { */ void HttpServer::addPathHandler( std::string method, - std::string pathExpr, + std::regex* pathExpr, void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. @@ -240,6 +241,36 @@ void HttpServer::addPathHandler( } // addPathHandler +/** + * @brief Register a handler for a path. + * + * When a browser request arrives, the request will contain a method (GET, POST, etc) and a path + * to be accessed. Using this method we can register a regular expression and, if the incoming method + * and path match the expression, the corresponding handler will be called. + * + * Example: + * @code{.cpp} + * static void handle_REST_WiFi(WebServer::HttpRequest *pRequest, WebServer::HttpResponse *pResponse) { + * ... + * } + * + * webServer.addPathHandler("GET", "/ESP32/WiFi", handle_REST_WiFi); + * @endcode + * + * @param [in] method The method being used for access ("GET", "POST" etc). + * @param [in] path The plain path being accessed. + * @param [in] handler The callback function to be invoked when a request arrives. + */ +void HttpServer::addPathHandler( + std::string method, + std::string path, + void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { + + // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. + m_pathHandlers.push_back(PathHandler(method, path, handler)); +} // addPathHandler + + /** * @brief Get the port number on which the HTTP Server is listening. * @return The port number on which the HTTP server is listening. @@ -333,15 +364,36 @@ void HttpServer::stop() { * @param [in] pathPattern The path pattern to be matched. * @param [in] webServerRequestHandler The request handler to be called. */ -PathHandler::PathHandler(std::string method, std::string pathPattern, +PathHandler::PathHandler(std::string method, std::regex *pRegex, + void (*pWebServerRequestHandler) + ( + HttpRequest* pHttpRequest, + HttpResponse* pHttpResponse) + ) { + m_method = method; // Save the method we are looking for. + m_pRegex = pRegex; // Save the Regex + m_textPattern = ""; // The plain text of the regex pattern. + m_isRegex = true; + m_pRequestHandler = pWebServerRequestHandler; // The handler to be invoked if the pattern matches. +} // PathHandler + + +/** + * @brief Construct an instance of a PathHandler. + * + * @param [in] method The method to be matched. + * @param [in] pathPattern The path to be matched. Must be an exact match. + * @param [in] webServerRequestHandler The request handler to be called. + */ +PathHandler::PathHandler(std::string method, std::string matchPath, void (*pWebServerRequestHandler) ( HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ) { m_method = method; // Save the method we are looking for. - m_pattern = std::regex(pathPattern); // Create the Regex pattern. - m_textPattern = pathPattern; // The plain text of the regex pattern. + m_textPattern = matchPath; + m_isRegex = false; m_pRequestHandler = pWebServerRequestHandler; // The handler to be invoked if the pattern matches. } // PathHandler @@ -354,11 +406,16 @@ PathHandler::PathHandler(std::string method, std::string pathPattern, * @return True if the path matches. */ bool PathHandler::match(std::string method, std::string path) { - ESP_LOGD("PathHandler", "matching: %s with %s", m_textPattern.c_str(), path.c_str()); if (method != m_method) { return false; } - return std::regex_search(path, m_pattern); + if (m_isRegex) { + ESP_LOGD("PathHandler", "regex matching: %s with %s", m_textPattern.c_str(), path.c_str()); + + return std::regex_search(path, *m_pRegex); + } + ESP_LOGD("PathHandler", "plain matching: %s with %s", m_textPattern.c_str(), path.c_str()); + return m_textPattern.compare(0, m_textPattern.length(), path) ==0; } // match diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index 35d13383..4b7e3083 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -34,11 +34,20 @@ class PathHandler { HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ); + PathHandler( + std::string method, // The method in the request to be matched. + std::regex* pathPattern, // The pattern in the request to be matched (regex) + void (*pWebServerRequestHandler) // The handler function to be invoked upon a match. + ( + HttpRequest* pHttpRequest, + HttpResponse* pHttpResponse) + ); bool match(std::string method, std::string path); // Does the request method and pattern match? void invokePathHandler(HttpRequest* request, HttpResponse* response); private: std::string m_method; - std::regex m_pattern; + std::regex* m_pRegex; + bool m_isRegex; std::string m_textPattern; void (*m_pRequestHandler)(HttpRequest* pHttpRequest, HttpResponse* pHttpResponse); }; // PathHandler @@ -57,6 +66,14 @@ class HttpServer { HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ); + void addPathHandler( + std::string method, + std::regex* pRegex, + void (*webServerRequestHandler) + ( + HttpRequest* pHttpRequest, + HttpResponse* pHttpResponse) + ); uint16_t getPort(); // Get the port on which the Http server is listening. std::string getRootPath(); // Get the root of the file system path. bool getSSL(); // Are we using SSL? diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp index 7180c648..c5e7028b 100644 --- a/cpp_utils/Memory.cpp +++ b/cpp_utils/Memory.cpp @@ -4,15 +4,95 @@ * Created on: Oct 24, 2017 * Author: kolban */ - +#include "sdkconfig.h" +#ifdef CONFIG_HEAP_TRACING #include "Memory.h" -Memory::Memory() { - // TODO Auto-generated constructor stub - +#include +#include "GeneralUtils.h" +extern "C" { +#include +#include } +#include + +static const char* LOG_TAG = "Memory"; + +heap_trace_record_t* Memory::m_pRecords = nullptr; +size_t Memory::m_lastHeapSize = 0; + +/** + * @brief Dump the trace records from the heap. + */ +void Memory::dump() { + ::heap_trace_dump(); +} // dump + -Memory::~Memory() { - // TODO Auto-generated destructor stub +/* STATIC */ void Memory::dumpHeapChange(std::string tag) { + size_t currentUsage = heap_caps_get_free_size(MALLOC_CAP_8BIT); + int diff = currentUsage - m_lastHeapSize; + ESP_LOGD(LOG_TAG, "%s: Heap changed by %d bytes (%d to %d)", tag.c_str(), diff, m_lastHeapSize, currentUsage); + m_lastHeapSize = currentUsage; } +/** + * @brief Initialize heap recording. + * @param [in] recordCount The maximum number of records to be recorded. + */ +void Memory::init(uint32_t recordCount) { + assert(recordCount > 0); + if (m_pRecords != nullptr) { + ESP_LOGE(LOG_TAG, "Already initialized"); + return; + } + + m_pRecords = new heap_trace_record_t[recordCount]; // Allocate the maximum number of records to be recorded. + if (m_pRecords == nullptr) { + ESP_LOGE(LOG_TAG, "Unable to create %d heap records", recordCount); + } + + esp_err_t errRc = ::heap_trace_init_standalone(m_pRecords, recordCount); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "heap_trace_init_standalone: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // init + + +void Memory::resumeTrace() { + esp_err_t errRc = ::heap_trace_resume(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "heap_trace_resume: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // resumeTrace + + +void Memory::startTraceAll() { + esp_err_t errRc = ::heap_trace_start(HEAP_TRACE_ALL); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // startTraceAll + + +void Memory::startTraceLeaks() { + esp_err_t errRc = ::heap_trace_start(HEAP_TRACE_LEAKS); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // startTraceLeaks + + +void Memory::stopTrace() { + esp_err_t errRc = ::heap_trace_stop(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // stopTrace + +#endif diff --git a/cpp_utils/Memory.h b/cpp_utils/Memory.h index 59510356..dca5a838 100644 --- a/cpp_utils/Memory.h +++ b/cpp_utils/Memory.h @@ -7,11 +7,27 @@ #ifndef COMPONENTS_CPP_UTILS_MEMORY_H_ #define COMPONENTS_CPP_UTILS_MEMORY_H_ +#include "sdkconfig.h" +#ifdef CONFIG_HEAP_TRACING +#include +extern "C" { +#include +} class Memory { public: - Memory(); - virtual ~Memory(); -}; + static void dump(); + static void dumpHeapChange(std::string tag); + static void init(uint32_t recordCount); + static void resumeTrace(); + static void startTraceAll(); + static void startTraceLeaks(); + static void stopTrace(); + +private: + static heap_trace_record_t* m_pRecords; + static size_t m_lastHeapSize; // Size of last heap recorded. +}; +#endif #endif /* COMPONENTS_CPP_UTILS_MEMORY_H_ */ diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index bb5c5069..94a90271 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -242,7 +242,7 @@ void WiFi::dump() { tcpip_adapter_ip_info_t WiFi::getApIpInfo() { //init(); tcpip_adapter_ip_info_t ipInfo; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); + ::tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); return ipInfo; } // getApIpInfo From 006c88c48a48801c498baf2c969f45388e2e49a1 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 31 Oct 2017 22:41:20 -0500 Subject: [PATCH 051/310] Aditions for #153 --- cpp_utils/JSON.cpp | 7 +++++++ cpp_utils/JSON.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 6fa866b5..72a8da70 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -6,6 +6,8 @@ */ +// See: https://github.com/DaveGamble/cJSON + #include #include #include "JSON.h" @@ -202,6 +204,11 @@ JsonObject::JsonObject(cJSON* node) { m_node = node; } // JsonObject +JsonArray JsonObject::getArray(std::string name) { + cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + return JsonArray(node); +} + /** * @brief Get the named boolean value from the object. diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index b41a22fb..ed009f8c 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -46,6 +46,9 @@ class JsonArray { void addString(std::string value); std::string toString(); std::size_t size(); +private: + friend class JSON; + friend class JsonObject; /** * @brief The underlying cJSON node. */ @@ -59,6 +62,7 @@ class JsonArray { class JsonObject { public: JsonObject(cJSON* node); + JsonArray getArray(std::string name); bool getBoolean(std::string name); double getDouble(std::string name); int getInt(std::string name); @@ -74,6 +78,9 @@ class JsonObject { void setString(std::string name, std::string value); std::string toString(); +private: + friend class JSON; + friend class JsonArray; /** * @brief The underlying cJSON node. */ From 190cfac536f74a782da7571c9f0b77610772ffff Mon Sep 17 00:00:00 2001 From: Philip Ashmore Date: Wed, 1 Nov 2017 16:21:58 +0000 Subject: [PATCH 052/310] Fixes for esp idf commit 79f206be47c3f608615c1de8c491107e6c9194bb and start of esp-idf integration --- Kconfig | 23 +++++++++++++++++++++++ component.mk | 35 +++++++++++++++++++++++++++++++++++ cpp_utils/BLEClient.h | 2 +- cpp_utils/BLEUtils.cpp | 16 ++++++---------- cpp_utils/Doxyfile | 2 +- 5 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 Kconfig create mode 100644 component.mk diff --git a/Kconfig b/Kconfig new file mode 100644 index 00000000..36b07fb6 --- /dev/null +++ b/Kconfig @@ -0,0 +1,23 @@ +menu "nkolban's c++ wrappers" + +menuconfig NKOLBAN + bool "nkolban's c++ wrappers" + default y + help + Select this option to use nkolban's c++ wrappers. + +menuconfig NKOLBAN_BLE + bool "bluetooth wrappers" + depends on BT_ENABLED && NKOLBAN + default y + help + Select this option to use nkolban's c++ bluetooth ble wrappers. + +menuconfig NKOLBAN_BLE2902 + bool "BLE2902" + depends on NKOLBAN_BLE + default y + help + Select this option to use nkolban's c++ bluetooth BLE2902 wrappers. + +endmenu \ No newline at end of file diff --git a/component.mk b/component.mk new file mode 100644 index 00000000..03383fab --- /dev/null +++ b/component.mk @@ -0,0 +1,35 @@ +c := cpp_utils +COMPONENT_EXTRA_INCLUDES := $(realpath $c) +COMPONENT_ADD_INCLUDEDIRS := $c +COMPONENT_SRCDIRS := $c + +$(call compile_only_if,$(CONFIG_NKOLBAN), \ +$c/Task.o \ +$c/FreeRTOS.o \ +$c/GeneralUtils.o \ +) + +$(call compile_only_if,$(CONFIG_NKOLBAN_BLE), \ +$c/BLEAddress.o \ +$c/BLEAdvertisedDevice.o \ +$c/BLEAdvertising.o \ +$c/BLECharacteristic.o \ +$c/BLECharacteristicCallbacks.o \ +$c/BLECharacteristicMap.o \ +$c/BLEClient.o \ +$c/BLEDescriptor.o \ +$c/BLEDescriptorMap.o \ +$c/BLEDevice.o \ +$c/BLERemoteCharacteristic.o \ +$c/BLERemoteDescriptor.o \ +$c/BLERemoteService.o \ +$c/BLEScan.o \ +$c/BLEServer.o \ +$c/BLEService.o \ +$c/BLEServiceMap.o \ +$c/BLEUUID.o \ +$c/BLEUtils.o \ +$c/BLEValue.o \ +) + +$(call compile_only_if,$(CONFIG_NKOLBAN_BLE2902),$c/BLE2902.o) diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 25704a34..b24c71ae 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include "BLERemoteService.h" #include "BLEService.h" #include "BLEAddress.h" diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index a91bbcc9..5d5c4c0a 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1286,8 +1286,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[staus: %s, conn_id: %d, remote_bda: %s]", - BLEUtils::gattStatusToString(evtParam->connect.status).c_str(), + ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str() ); @@ -1302,8 +1301,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[staus: %s, conn_id: %d, remote_bda: %s]", - BLEUtils::gattStatusToString(evtParam->disconnect.status).c_str(), + ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->disconnect.conn_id, BLEAddress(evtParam->disconnect.remote_bda).toString().c_str() ); @@ -1586,10 +1584,9 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_CONGEST_EVT case ESP_GATTS_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, is_connected: %d]", + ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, - BLEAddress(evtParam->connect.remote_bda).toString().c_str(), - evtParam->connect.is_connected); + BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; } // ESP_GATTS_CONNECT_EVT @@ -1603,10 +1600,9 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_CREATE_EVT case ESP_GATTS_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, is_connected: %d]", + ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, - BLEAddress(evtParam->connect.remote_bda).toString().c_str(), - evtParam->connect.is_connected); + BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; } // ESP_GATTS_DISCONNECT_EVT diff --git a/cpp_utils/Doxyfile b/cpp_utils/Doxyfile index 5ac15096..cd186876 100644 --- a/cpp_utils/Doxyfile +++ b/cpp_utils/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /home/kolban/esp32/esptest/apps/workspace/esp32-snippets/cpp_utils/docs +OUTPUT_DIRECTORY = docs # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and From 79d58a0f55ad0efbebbf59b3ec5791b5fc763b87 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 2 Nov 2017 01:24:18 +0100 Subject: [PATCH 053/310] Fix issue #157 --- .gitignore | 3 +++ cpp_utils/BLEClient.cpp | 1 + cpp_utils/BLEDevice.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..46b14a0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.project +.cproject +.settings/ diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 721e5efb..ba858287 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -117,6 +117,7 @@ void BLEClient::disconnect() { ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } + esp_ble_gattc_app_unregister(getGattcIf()); m_peerAddress = BLEAddress("00:00:00:00:00:00"); ESP_LOGD(LOG_TAG, "<< disconnect()"); } // disconnect diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index e58cac7f..199210a2 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -187,7 +187,7 @@ void BLEDevice::init(std::string deviceName) { return; } - errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; From e2555685f5503f39d1c2369df39cd0ddda4896d0 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 1 Nov 2017 20:51:00 -0500 Subject: [PATCH 054/310] Change semaphore usage to FreeRTIS #156 --- cpp_utils/FreeRTOS.cpp | 2 +- cpp_utils/HttpServer.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 4097237a..b8384512 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -96,7 +96,7 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { FreeRTOS::Semaphore::Semaphore(std::string name) { - m_usePthreads = true; + m_usePthreads = false; // Are we using pThreads or FreeRTOS? if (m_usePthreads) { pthread_mutex_init(&m_pthread_mutex, nullptr); } else { diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 5578993f..9149ec47 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -12,7 +12,6 @@ #include #include "HttpRequest.h" #include "HttpResponse.h" -#include "Memory.h" #include "FileSystem.h" #include "WebSocket.h" #include "GeneralUtils.h" From 6aac6e08fef931b5960e35c78182c163b7b790ee Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 2 Nov 2017 18:51:57 -0500 Subject: [PATCH 055/310] Changes for #162 --- cpp_utils/BLEUtils.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 5d5c4c0a..4a08c7c6 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -33,7 +33,7 @@ static std::map g_connIdMap; typedef struct { uint32_t assignedNumber; - std::string name; + const char* name; } member_t; static const member_t members_ids[] = { @@ -291,7 +291,7 @@ static const member_t members_ids[] = { typedef struct { uint32_t assignedNumber; - std::string name; + const char* name; } gattdescriptor_t; static const gattdescriptor_t g_descriptor_ids[] = { @@ -315,7 +315,7 @@ static const gattdescriptor_t g_descriptor_ids[] = { typedef struct { uint32_t assignedNumber; - std::string name; + const char* name; } characteristicMap_t; static const characteristicMap_t g_characteristicsMappings[] = { @@ -542,8 +542,8 @@ static const characteristicMap_t g_characteristicsMappings[] = { * @brief Mapping from service ids to names */ typedef struct { - std::string name; - std::string type; + const char* name; + const char* type; uint32_t assignedNumber; } gattService_t; @@ -1821,9 +1821,9 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { const characteristicMap_t *p = g_characteristicsMappings; - while (p->name.length() > 0) { + while (strlen(p->name) > 0) { if (p->assignedNumber == characteristicUUID) { - return p->name; + return std::string(p->name); } p++; } @@ -1838,9 +1838,9 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID */ std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { gattdescriptor_t* p = (gattdescriptor_t *)g_descriptor_ids; - while (p->name.length() > 0) { + while (strlen(p->name) > 0) { if (p->assignedNumber == descriptorUUID) { - return p->name; + return std::string(p->name); } p++; } @@ -1874,9 +1874,9 @@ std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { std::string BLEUtils::gattServiceToString(uint32_t serviceId) { gattService_t* p = (gattService_t *)g_gattServices; - while (p->name.length() > 0) { + while (strlen(p->name) > 0) { if (p->assignedNumber == serviceId) { - return p->name; + return std::string(p->name); } p++; } @@ -1988,9 +1988,9 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { std::string BLEUtils::getMember(uint32_t memberId) { member_t* p = (member_t *)members_ids; - while (p->name.length() > 0) { + while (strlen(p->name) > 0) { if (p->assignedNumber == memberId) { - return p->name; + return std::string(p->name); } p++; } From 2e3283297bcd4b0eb1de5f9aa4973e761aa7639d Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 2 Nov 2017 23:18:21 -0500 Subject: [PATCH 056/310] Save ram for #162 --- cpp_utils/BLEDevice.cpp | 3 ++- cpp_utils/HttpRequest.cpp | 48 +++++++++++++++++++-------------------- cpp_utils/HttpRequest.h | 46 ++++++++++++++++++------------------- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 199210a2..3ab2c7cf 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -159,7 +159,8 @@ void BLEDevice::gapEventHandler( /** * @brief Retrieve the Scan object that we use for scanning. - * @return The scanning object reference. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. */ BLEScan* BLEDevice::getScan() { if (m_pScan == nullptr) { diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index 45655893..efc8d096 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -45,30 +45,30 @@ static const char* LOG_TAG="HttpRequest"; //static std::string lineTerminator = "\r\n"; -const std::string HttpRequest::HTTP_HEADER_ACCEPT = "Accept"; -const std::string HttpRequest::HTTP_HEADER_ALLOW = "Allow"; -const std::string HttpRequest::HTTP_HEADER_CONNECTION = "Connection"; -const std::string HttpRequest::HTTP_HEADER_CONTENT_LENGTH = "Content-Length"; -const std::string HttpRequest::HTTP_HEADER_CONTENT_TYPE = "Content-Type"; -const std::string HttpRequest::HTTP_HEADER_COOKIE = "Cookie"; -const std::string HttpRequest::HTTP_HEADER_HOST = "Host"; -const std::string HttpRequest::HTTP_HEADER_LAST_MODIFIED = "Last-Modified"; -const std::string HttpRequest::HTTP_HEADER_ORIGIN = "Origin"; -const std::string HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; -const std::string HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; -const std::string HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; -const std::string HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; -const std::string HttpRequest::HTTP_HEADER_UPGRADE = "Upgrade"; -const std::string HttpRequest::HTTP_HEADER_USER_AGENT = "User-Agent"; - -const std::string HttpRequest::HTTP_METHOD_CONNECT = "CONNECT"; -const std::string HttpRequest::HTTP_METHOD_DELETE = "DELETE"; -const std::string HttpRequest::HTTP_METHOD_GET = "GET"; -const std::string HttpRequest::HTTP_METHOD_HEAD = "HEAD"; -const std::string HttpRequest::HTTP_METHOD_OPTIONS = "OPTIONS"; -const std::string HttpRequest::HTTP_METHOD_PATCH = "PATCH"; -const std::string HttpRequest::HTTP_METHOD_POST = "POST"; -const std::string HttpRequest::HTTP_METHOD_PUT = "PUT"; +const char HttpRequest::HTTP_HEADER_ACCEPT[] = "Accept"; +const char HttpRequest::HTTP_HEADER_ALLOW[] = "Allow"; +const char HttpRequest::HTTP_HEADER_CONNECTION[] = "Connection"; +const char HttpRequest::HTTP_HEADER_CONTENT_LENGTH[] = "Content-Length"; +const char HttpRequest::HTTP_HEADER_CONTENT_TYPE[] = "Content-Type"; +const char HttpRequest::HTTP_HEADER_COOKIE[] = "Cookie"; +const char HttpRequest::HTTP_HEADER_HOST[] = "Host"; +const char HttpRequest::HTTP_HEADER_LAST_MODIFIED[] = "Last-Modified"; +const char HttpRequest::HTTP_HEADER_ORIGIN[] = "Origin"; +const char HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_ACCEPT[] = "Sec-WebSocket-Accept"; +const char HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL[] = "Sec-WebSocket-Protocol"; +const char HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_KEY[] = "Sec-WebSocket-Key"; +const char HttpRequest::HTTP_HEADER_SEC_WEBSOCKET_VERSION[] = "Sec-WebSocket-Version"; +const char HttpRequest::HTTP_HEADER_UPGRADE[] = "Upgrade"; +const char HttpRequest::HTTP_HEADER_USER_AGENT[] = "User-Agent"; + +const char HttpRequest::HTTP_METHOD_CONNECT[] = "CONNECT"; +const char HttpRequest::HTTP_METHOD_DELETE[] = "DELETE"; +const char HttpRequest::HTTP_METHOD_GET[] = "GET"; +const char HttpRequest::HTTP_METHOD_HEAD[] = "HEAD"; +const char HttpRequest::HTTP_METHOD_OPTIONS[] = "OPTIONS"; +const char HttpRequest::HTTP_METHOD_PATCH[] = "PATCH"; +const char HttpRequest::HTTP_METHOD_POST[] = "POST"; +const char HttpRequest::HTTP_METHOD_PUT[] = "PUT"; diff --git a/cpp_utils/HttpRequest.h b/cpp_utils/HttpRequest.h index 3f989cbc..a5a81038 100644 --- a/cpp_utils/HttpRequest.h +++ b/cpp_utils/HttpRequest.h @@ -27,30 +27,30 @@ class HttpRequest { HttpRequest(Socket s); virtual ~HttpRequest(); - static const std::string HTTP_HEADER_ACCEPT; - static const std::string HTTP_HEADER_ALLOW; - static const std::string HTTP_HEADER_CONNECTION; - static const std::string HTTP_HEADER_CONTENT_LENGTH; - static const std::string HTTP_HEADER_CONTENT_TYPE; - static const std::string HTTP_HEADER_COOKIE; - static const std::string HTTP_HEADER_HOST; - static const std::string HTTP_HEADER_LAST_MODIFIED; - static const std::string HTTP_HEADER_ORIGIN; - static const std::string HTTP_HEADER_SEC_WEBSOCKET_ACCEPT; - static const std::string HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL; - static const std::string HTTP_HEADER_SEC_WEBSOCKET_KEY; - static const std::string HTTP_HEADER_SEC_WEBSOCKET_VERSION; - static const std::string HTTP_HEADER_UPGRADE; - static const std::string HTTP_HEADER_USER_AGENT; + static const char HTTP_HEADER_ACCEPT[]; + static const char HTTP_HEADER_ALLOW[]; + static const char HTTP_HEADER_CONNECTION[]; + static const char HTTP_HEADER_CONTENT_LENGTH[]; + static const char HTTP_HEADER_CONTENT_TYPE[]; + static const char HTTP_HEADER_COOKIE[]; + static const char HTTP_HEADER_HOST[]; + static const char HTTP_HEADER_LAST_MODIFIED[]; + static const char HTTP_HEADER_ORIGIN[]; + static const char HTTP_HEADER_SEC_WEBSOCKET_ACCEPT[]; + static const char HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL[]; + static const char HTTP_HEADER_SEC_WEBSOCKET_KEY[]; + static const char HTTP_HEADER_SEC_WEBSOCKET_VERSION[]; + static const char HTTP_HEADER_UPGRADE[]; + static const char HTTP_HEADER_USER_AGENT[]; - static const std::string HTTP_METHOD_CONNECT; - static const std::string HTTP_METHOD_DELETE; - static const std::string HTTP_METHOD_GET; - static const std::string HTTP_METHOD_HEAD; - static const std::string HTTP_METHOD_OPTIONS; - static const std::string HTTP_METHOD_PATCH; - static const std::string HTTP_METHOD_POST; - static const std::string HTTP_METHOD_PUT; + static const char HTTP_METHOD_CONNECT[]; + static const char HTTP_METHOD_DELETE[]; + static const char HTTP_METHOD_GET[]; + static const char HTTP_METHOD_HEAD[]; + static const char HTTP_METHOD_OPTIONS[]; + static const char HTTP_METHOD_PATCH[]; + static const char HTTP_METHOD_POST[]; + static const char HTTP_METHOD_PUT[]; void close(); // Close the connection to the client. void dump(); // Diagnostic dump of the Http request. From 90167ad75f9fa1cbdc93384e92e92416ebfd117c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 3 Nov 2017 23:07:49 -0500 Subject: [PATCH 057/310] Fixes for #166 --- cpp_utils/CPPNVS.cpp | 13 ++++++-- cpp_utils/HttpRequest.cpp | 2 +- networking/bootwifi/BootWiFi.cpp | 50 ++++++++++++++++++++++++------- networking/bootwifi/README.md | 51 +++++++++----------------------- 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index 99588a43..d0a2a0bb 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -6,24 +6,33 @@ */ #include "CPPNVS.h" +#include "GeneralUtils.h" +#include #include #include #include -const char* LOG_TAG = "CPPNVS"; +static const char LOG_TAG[] = "CPPNVS"; /** * @brief Constructor. * * @param [in] name The namespace to open for access. - * @param [in] openMode + * @param [in] openMode The open mode. One of NVS_READWRITE (default) or NVS_READONLY. */ NVS::NVS(std::string name, nvs_open_mode openMode) { + esp_err_t errRc = ::nvs_flash_init(); // Initialize flash + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } m_name = name; ::nvs_open(name.c_str(), openMode, &m_handle); } // NVS +/** + * @brief Desctructor + */ NVS::~NVS() { ::nvs_close(m_handle); } // ~NVS diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index efc8d096..5347f195 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -299,7 +299,7 @@ std::map HttpRequest::parseForm() { std::getline(currentPair, name, '='); // Parse the current form entry into name/value. currentPair >> value; // The value is what remains. map[name] = urlDecode(value); // Decode the field which may have been encoded. - ESP_LOGD(LOG_TAG, " %s = %s", name.c_str(), map[name].c_str()); // Debug + ESP_LOGD(LOG_TAG, " %s = \"%s\"", name.c_str(), map[name].c_str()); // Debug // Add the form entry into the map. } // Processed all form entries. ESP_LOGD(LOG_TAG, "<< parseForm"); // Debug diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index a6f8ebb6..531d1e55 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -45,18 +45,22 @@ typedef struct { tcpip_adapter_ip_info_t ipInfo; // Optional static IP information } connection_info_t; -static bootwifi_callback_t g_callback = NULL; // Callback function to be invoked when we have finished. +//static bootwifi_callback_t g_callback = NULL; // Callback function to be invoked when we have finished. // Forward declarations static void saveConnectionInfo(connection_info_t *pConnectionInfo); -static const char* LOG_TAG = "bootwifi"; +static const char LOG_TAG[] = "bootwifi"; static void dumpConnectionInfo(connection_info_t *pConnectionInfo) { ESP_LOGD(LOG_TAG, "connection_info.ssid = %.*s", SSID_SIZE, pConnectionInfo->ssid); ESP_LOGD(LOG_TAG, "connection_info.password = %.*s", PASSWORD_SIZE, pConnectionInfo->password); + ESP_LOGD(LOG_TAG, "ip: %s, gw: %s, netmask: %s", + GeneralUtils::ipToString((uint8_t*)&pConnectionInfo->ipInfo.ip).c_str(), + GeneralUtils::ipToString((uint8_t*)&pConnectionInfo->ipInfo.gw).c_str(), + GeneralUtils::ipToString((uint8_t*)&pConnectionInfo->ipInfo.netmask).c_str()); } @@ -128,6 +132,7 @@ static void sendForm(HttpRequest* pRequest, HttpResponse* pResponse) { pResponse->close(); } // sendForm + static void copyData(uint8_t* pTarget, size_t targetLength, std::string source) { memset(pTarget, 0, targetLength); size_t copySize = (source.length() > targetLength)? targetLength:source.length(); @@ -135,8 +140,12 @@ static void copyData(uint8_t* pTarget, size_t targetLength, std::string source) if (copySize < targetLength) { pTarget[copySize] = '\0'; } -} +} // copyData + +/** + * @brief Process the form response. + */ static void processForm(HttpRequest* pRequest, HttpResponse* pResponse) { ESP_LOGD(LOG_TAG, ">> processForm"); std::map formMap = pRequest->parseForm(); @@ -145,20 +154,41 @@ static void processForm(HttpRequest* pRequest, HttpResponse* pResponse) { copyData((uint8_t*)connectionInfo.password, PASSWORD_SIZE, formMap["password"]); try { - inet_pton(AF_INET, formMap["ip"].c_str(), &connectionInfo.ipInfo.ip); - } catch(std::out_of_range e) { + std::string ipStr = formMap.at("ip"); + if (ipStr.empty()) { + ESP_LOGD(LOG_TAG, "No IP address using default 0.0.0.0"); + connectionInfo.ipInfo.ip.addr = 0; + } else { + inet_pton(AF_INET, ipStr.c_str(), &connectionInfo.ipInfo.ip); + } + } catch(std::out_of_range& e) { + ESP_LOGD(LOG_TAG, "No IP address using default 0.0.0.0"); connectionInfo.ipInfo.ip.addr = 0; } try { - inet_pton(AF_INET, formMap["gw"].c_str(), &connectionInfo.ipInfo.gw); - } catch(std::out_of_range e) { + std::string gwStr = formMap.at("gw"); + if (gwStr.empty()) { + ESP_LOGD(LOG_TAG, "No GW address using default 0.0.0.0"); + connectionInfo.ipInfo.gw.addr = 0; + } else { + inet_pton(AF_INET, gwStr.c_str(), &connectionInfo.ipInfo.gw); + } + } catch(std::out_of_range& e) { + ESP_LOGD(LOG_TAG, "No GW address using default 0.0.0.0"); connectionInfo.ipInfo.gw.addr = 0; } try { - inet_pton(AF_INET, formMap["netmask"].c_str(), &connectionInfo.ipInfo.netmask); - } catch(std::out_of_range e) { + std::string netmaskStr = formMap.at("netmask"); + if (netmaskStr.empty()) { + ESP_LOGD(LOG_TAG, "No Netmask address using default 0.0.0.0"); + connectionInfo.ipInfo.netmask.addr = 0; + } else { + inet_pton(AF_INET, netmaskStr.c_str(), &connectionInfo.ipInfo.netmask); + } + } catch(std::out_of_range& e) { + ESP_LOGD(LOG_TAG, "No Netmask address using default 0.0.0.0"); connectionInfo.ipInfo.netmask.addr = 0; } @@ -281,5 +311,5 @@ void BootWiFi::boot() { BootWiFi::BootWiFi() { m_httpServerStarted = false; - setAccessPointCredentials("esp32", "password"); + setAccessPointCredentials("esp32", "password"); // Default access point credentials } diff --git a/networking/bootwifi/README.md b/networking/bootwifi/README.md index d46d43d4..31129bb8 100644 --- a/networking/bootwifi/README.md +++ b/networking/bootwifi/README.md @@ -1,47 +1,25 @@ # Bootwifi -It is common to want to start an ESP32 and have it connect to a WiFi environment but how -does one bootstrap it? To connect to a WiFi environment, we typically need to know the -SSID and password of the network to which we wish to connect. But without network connection -to the ESP32, how do we set it? This component provides a potential solution. +It is common to want to start an ESP32 and have it connect to a WiFi environment but how does one bootstrap it? To connect to a WiFi environment, we typically need to know the SSID and password of the network to which we wish to connect. But without network connection to the ESP32, how do we set it? This component provides a potential solution. -The module exposes a function called `bootwifi` which, when called, will own the connection -of the device to the local WiFi environment. To do this, it looks in its flash storage to see -if it has previously been given an SSID/password pair. If it has, it attempts to connect to that -access point and join the network. +The module exposes a function called `bootwifi` which, when called, will own the connection of the device to the local WiFi environment. To do this, it looks in its flash storage to see if it has previously been given an SSID/password pair. If it has, it attempts to connect to that access point and join the network. -However, let us assume that it has never been given that information. In this case it will become -an access point in its own right. Now you can connect to the ESP32 using your phone or other WiFi -device as it will appear as an access point against which we can connect. Once connected, we can -open a browser to it. In the browser page, we will be prompted for the SSID and password we wish -to subsequently use. This will be saved and used from then on. Now the device will connect to that -network. +However, let us assume that it has never been given that information. In this case it will become an access point in its own right. What this means is that the ESP32 will become a WiFi network to which other WiFi devices can connect. Now you can connect to the ESP32 using your phone or other WiFi device as it will appear as an access point against which we can connect. Once connected, we can open a browser to it. In the browser page, we will be prompted for the SSID and password we wish to subsequently use. This will be saved and used from then on. Now the device will connect to that network. -What if we take our ESP32 to a new environment where the previously saved access point is no longer -accessible or we simply just fail to connect? Again, we will fall back into being an access point -and the user will be able to supply new information. +What if we take our ESP32 to a new environment where the previously saved access point is no longer accessible or we simply just fail to connect? Again, we will fall back into being an access point and the user will be able to supply new information. -What if we want to change the access point to which the ESP32 connects even if that access point has -been previously saved and is still connectable? Simple, the ESP32 can check a GPIO pin at startup and, -if that pin is high (default low) then that can be used as a manual indication that we should become -an access point without even attempting to connect to the network. +What if we want to change the access point to which the ESP32 connects even if that access point has been previously saved and is still connectable? Simple, the ESP32 can check a GPIO pin at startup and, if that pin is high (default low) then that can be used as a manual indication that we should become an access point without even attempting to connect to the network. -This code is supplied in the form of an ESP-IDF module. It depends on a partner module called `mongoose` -that provides a Web Server in order to server up the web pages. A build of `mongoose` is available -however, [Cesanta](https://www.cesanta.com/), the makers of Mongoose are still working on a formal -port to the ESP32 which is anticipated to be available before 2017 so we should really wait for that -to become available. +This code is supplied in the form of an ESP-IDF module. + +The logic uses C++ exception handling and hence C++ exception handling must be enabled in `make menuconfig`. ## GPIO boot override -To enable the ability to specify a GPIO pin to override known station information, compile -the code with `-DBOOTWIFI_OVERRIDE_GPIO=` when `` is a GPIO pin number. If the -pin is high at startup, then it will override. The pin is configured as pull-down low so -it need not be artificially held low. The default is no override pin. +To enable the ability to specify a GPIO pin to override known station information, compile the code with `-DBOOTWIFI_OVERRIDE_GPIO=` when `` is a GPIO pin number. If the pin is high at startup, then it will override. The pin is configured as pull-down low so it need not be artificially held low. The default is no override pin. ## Future enhancements There is always room for enhancements: -* Improve the web page shown to the user - Right now it is pretty basic and ideally could be -dramatically improved. Features to be added include +* Improve the web page shown to the user - Right now it is pretty basic and ideally could be dramatically improved. Features to be added include - listing of available access points for selection * Integrate SSL security. * NeoPixel support for visualization of connection status: @@ -57,13 +35,12 @@ dramatically improved. Features to be added include ## Design and implementation notes The parameters for Bootwifi are stored in Non-Volatile Storage (NVS). The name space in NVS -is "bootwifi". The keys are: +is `bootwifi`. The keys are: -* version - The version of the protocol. -* connectionInfo - The details for connection. +* `version` - The version of the protocol. +* `connectionInfo` - The details for connection. -The form shown to the end user sends back a response as an HTTP POST to "/ssidSelected". -which contains the following form fields: +The form shown to the end user sends back a response as an HTTP POST to `/ssidSelected`. which contains the following form fields: * ssid * password From c1011fdc090a728266ca69a6cddcc8a2a840157a Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 7 Nov 2017 04:18:17 +0100 Subject: [PATCH 058/310] Add fromString to BLEUUID --- cpp_utils/BLEDevice.cpp | 121 +++++++++++++++++++++------------------- cpp_utils/BLEUUID.cpp | 19 +++++++ cpp_utils/BLEUUID.h | 1 + 3 files changed, 85 insertions(+), 56 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 3ab2c7cf..8d7e04f1 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -39,7 +39,7 @@ static const char* LOG_TAG = "BLEDevice"; BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; - +bool initialized = false; /** * @brief Create a new instance of a client. * @return A new instance of the client. @@ -175,68 +175,77 @@ BLEScan* BLEDevice::getScan() { * @param deviceName The device name of the device. */ void BLEDevice::init(std::string deviceName) { - esp_err_t errRc = ::nvs_flash_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - errRc = esp_bt_controller_init(&bt_cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + if(!initialized){ + initialized = true; + esp_err_t errRc = ::nvs_flash_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + errRc = esp_bt_controller_init(&bt_cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#ifndef CLASSIC_BT_ENABLED + // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#else + errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif + errRc = esp_bluedroid_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_bluedroid_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + errRc = esp_bluedroid_enable(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_bluedroid_enable(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } - errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; + + esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; + errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; } - - errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; - - esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; - errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; - vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. } // init diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 19da5185..6b2311a7 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -144,6 +144,7 @@ BLEUUID::BLEUUID(uint16_t uuid) { m_uuid.len = ESP_UUID_LEN_16; m_uuid.uuid.uuid16 = uuid; m_valueSet = true; + } // BLEUUID @@ -345,4 +346,22 @@ std::string BLEUUID::toString() { std::setw(2) << (int)m_uuid.uuid.uuid128[0]; return ss.str(); } // toString + +BLEUUID BLEUUID::fromString(std::string _uuid){ + uint8_t start = 0; + if(strstr(_uuid.c_str(), "0x") != nullptr){ + start = 2; + } + uint8_t len = _uuid.length() - start; + if(len == 4 ){ + uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + }else if(len == 8){ + uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + }else if (len == 36){ + return BLEUUID(_uuid); + } + return BLEUUID(); +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index a11220b9..bbe9b872 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -28,6 +28,7 @@ class BLEUUID { esp_bt_uuid_t* getNative(); BLEUUID to128(); std::string toString(); + static BLEUUID fromString(std::string uuid); private: esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. From b22f8250286b7c860336cae2851cb5a9e7773248 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 17:55:39 -0600 Subject: [PATCH 059/310] sync --- cpp_utils/BLEDevice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 3ab2c7cf..41d928f1 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -163,9 +163,12 @@ void BLEDevice::gapEventHandler( * try and release/delete it. */ BLEScan* BLEDevice::getScan() { + //ESP_LOGD(LOG_TAG, ">> getScan"); if (m_pScan == nullptr) { m_pScan = new BLEScan(); + //ESP_LOGD(LOG_TAG, " - creating a new scan object"); } + //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); return m_pScan; } // getScan From 20e042548dc87d7058642d745ef9470948bc3975 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 18:07:11 -0600 Subject: [PATCH 060/310] #176 --- cpp_utils/BLEScan.cpp | 5 +- cpp_utils/BLEScan.h | 3 +- cpp_utils/FreeRTOS.cpp | 21 ++++--- cpp_utils/FreeRTOS.h | 15 ++--- cpp_utils/I2C.cpp | 92 +++++++++++++++++++++------- cpp_utils/I2C.h | 9 ++- cpp_utils/JSON.h | 5 +- cpp_utils/System.cpp | 6 +- mongoose/webserver/main/component.mk | 2 +- 9 files changed, 112 insertions(+), 46 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index d3157b7f..176f5a0c 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -177,7 +177,7 @@ void BLEScan::setWindow(uint16_t windowMSecs) { BLEScanResults BLEScan::start(uint32_t duration) { ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); - m_semaphoreScanEnd.take("start"); + m_semaphoreScanEnd.take(std::string("start")); m_scanResults.m_vectorAdvertisedDevices.clear(); @@ -199,8 +199,7 @@ BLEScanResults BLEScan::start(uint32_t duration) { m_stopped = false; - m_semaphoreScanEnd.take("start"); - m_semaphoreScanEnd.give(); + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. ESP_LOGD(LOG_TAG, "<< start()"); return m_scanResults; diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index bc7f4314..5f0d5962 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -46,8 +46,6 @@ class BLEScanResults { */ class BLEScan { public: - BLEScan(); - void setActiveScan(bool active); void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks); void setInterval(uint16_t intervalMSecs); @@ -56,6 +54,7 @@ class BLEScan { void stop(); private: + BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. friend class BLEDevice; void gapEventHandler( esp_gap_ble_cb_event_t event, diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index b8384512..3a80c698 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -76,21 +76,25 @@ uint32_t FreeRTOS::getTimeSinceStart() { * @return The value associated with the semaphore. */ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { - ESP_LOGV(LOG_TAG, "Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); } else { xSemaphoreTake(m_semaphore, portMAX_DELAY); } + m_owner = owner; + if (m_usePthreads) { pthread_mutex_unlock(&m_pthread_mutex); } else { xSemaphoreGive(m_semaphore); } - ESP_LOGV(LOG_TAG, "Semaphore released: %s", toString().c_str()); - m_owner = ""; + ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); + m_owner = std::string(""); return m_value; } // wait @@ -104,7 +108,7 @@ FreeRTOS::Semaphore::Semaphore(std::string name) { } m_name = name; - m_owner = ""; + m_owner = std::string(""); m_value = 0; } @@ -123,6 +127,7 @@ FreeRTOS::Semaphore::~Semaphore() { * The Semaphore is given. */ void FreeRTOS::Semaphore::give() { + ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); if (m_usePthreads) { pthread_mutex_unlock(&m_pthread_mutex); } else { @@ -131,8 +136,8 @@ void FreeRTOS::Semaphore::give() { #ifdef ARDUINO_ARCH_ESP32 FreeRTOS::sleep(10); #endif - ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); - m_owner = ""; + + m_owner = std::string(""); } // Semaphore::give @@ -183,13 +188,15 @@ void FreeRTOS::Semaphore::take(std::string owner) * @param [in] timeoutMs Timeout in milliseconds. */ void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { + ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); - m_owner = owner; + if (m_usePthreads) { assert(false); } else { xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); } + m_owner = owner; ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index fe318601..30d33be7 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -31,14 +31,15 @@ class FreeRTOS { public: Semaphore(std::string owner = ""); ~Semaphore(); - void give(); - void giveFromISR(); - void give(uint32_t value); - void setName(std::string name); - void take(std::string owner=""); - void take(uint32_t timeoutMs, std::string owner=""); - uint32_t wait(std::string owner=""); + void give(); + void give(uint32_t value); + void giveFromISR(); + void setName(std::string name); + void take(std::string owner=""); + void take(uint32_t timeoutMs, std::string owner=""); std::string toString(); + uint32_t wait(std::string owner=""); + private: SemaphoreHandle_t m_semaphore; pthread_mutex_t m_pthread_mutex; diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index d6a7121a..6f82e6a1 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -12,6 +12,7 @@ #include "I2C.h" #include "sdkconfig.h" #include +#include "GeneralUtils.h" static const char* LOG_TAG = "I2C"; @@ -27,6 +28,7 @@ I2C::I2C() { m_cmd = 0; m_sdaPin = DEFAULT_SDA_PIN; m_sclPin = DEFAULT_CLK_PIN; + m_portNum = I2C_NUM_0; } // I2C @@ -41,7 +43,10 @@ void I2C::beginTransaction() { ESP_LOGD(LOG_TAG, "beginTransaction()"); } m_cmd = ::i2c_cmd_link_create(); - ESP_ERROR_CHECK(::i2c_master_start(m_cmd)); + esp_err_t errRc = ::i2c_master_start(m_cmd); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } m_directionKnown = false; } // beginTransaction @@ -57,8 +62,15 @@ void I2C::endTransaction() { if (debug) { ESP_LOGD(LOG_TAG, "endTransaction()"); } - ESP_ERROR_CHECK(::i2c_master_stop(m_cmd)); - ESP_ERROR_CHECK(::i2c_master_cmd_begin(I2C_NUM_0, m_cmd, 1000/portTICK_PERIOD_MS)); + esp_err_t errRc = ::i2c_master_stop(m_cmd); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + + errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000/portTICK_PERIOD_MS); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } ::i2c_cmd_link_delete(m_cmd); m_directionKnown = false; } // endTransaction @@ -83,21 +95,30 @@ uint8_t I2C::getAddress() const * @param [in] sclPin The pin to use for SCL clock. * @return N/A. */ -void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin) { - ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d", address, sdaPin, sclPin); - this->m_sdaPin = sdaPin; - this->m_sclPin = sclPin; - this->m_address = address; +void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum) { + ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d, clockSpeed=%d, portNum=%d", address, sdaPin, sclPin, clockSpeed, portNum); + asser(portNum < I2C_NUM_MAX); + m_portNum = portNum; + m_sdaPin = sdaPin; + m_sclPin = sclPin; + m_address = address; + i2c_config_t conf; - conf.mode = I2C_MODE_MASTER; + conf.mode = I2C_MODE_MASTER; conf.sda_io_num = sdaPin; conf.scl_io_num = sclPin; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; - conf.master.clk_speed = 100000; - ESP_ERROR_CHECK(::i2c_param_config(I2C_NUM_0, &conf)); + conf.master.clk_speed = 100000; + esp_err_t errRc = ::i2c_param_config(m_portNum, &conf); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_param_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } if (!driverInstalled) { - ESP_ERROR_CHECK(::i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); + errRc = ::i2c_driver_install(m_portNum, I2C_MODE_MASTER, 0, 0, 0); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_driver_install: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } driverInstalled = true; } } // init @@ -117,9 +138,15 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { } if (m_directionKnown == false) { m_directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack)); + esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + esp_err_t errRc = ::i2c_master_read(m_cmd, bytes, length, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_read: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - ESP_ERROR_CHECK(::i2c_master_read(m_cmd, bytes, length, !ack)); } // read @@ -136,7 +163,10 @@ void I2C::read(uint8_t *byte, bool ack) { } if (m_directionKnown == false) { m_directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack)); + esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } } ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, !ack)); } // readByte @@ -200,7 +230,7 @@ bool I2C::slavePresent(uint8_t address) { ::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); ::i2c_master_stop(cmd); - esp_err_t espRc = ::i2c_master_cmd_begin(I2C_NUM_0, cmd, 100/portTICK_PERIOD_MS); + esp_err_t espRc = ::i2c_master_cmd_begin(m_portNum, cmd, 100/portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); return espRc == 0; // Return true if the slave is present and false otherwise. } // slavePresent @@ -214,7 +244,10 @@ void I2C::start() { if (debug) { ESP_LOGD(LOG_TAG, "start()"); } - ESP_ERROR_CHECK(::i2c_master_start(m_cmd)); + esp_err_t errRc = ::i2c_master_start(m_cmd); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } } // start @@ -226,7 +259,10 @@ void I2C::stop() { if (debug) { ESP_LOGD(LOG_TAG, "stop()"); } - ESP_ERROR_CHECK(::i2c_master_stop(m_cmd)); + esp_err_t errRc = ::i2c_master_stop(m_cmd); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } m_directionKnown = false; } // stop @@ -244,9 +280,15 @@ void I2C::write(uint8_t byte, bool ack) { } if (m_directionKnown == false) { m_directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack)); + esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + esp_err_t errRc = ::i2c_master_write_byte(m_cmd, byte, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, byte, !ack)); } // write @@ -264,9 +306,15 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { } if (m_directionKnown == false) { m_directionKnown = true; - ESP_ERROR_CHECK(::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack)); + esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + esp_err_t errRc = ::i2c_master_write(m_cmd, bytes, length, !ack); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "i2c_master_write: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - ESP_ERROR_CHECK(::i2c_master_write(m_cmd, bytes, length, !ack)); } // write diff --git a/cpp_utils/I2C.h b/cpp_utils/I2C.h index 5d38af67..9886d63a 100644 --- a/cpp_utils/I2C.h +++ b/cpp_utils/I2C.h @@ -23,22 +23,29 @@ class I2C { bool m_directionKnown; gpio_num_t m_sdaPin; gpio_num_t m_sclPin; + i2c_port_t m_portNum; public: /** * @brief The default SDA pin. */ static const gpio_num_t DEFAULT_SDA_PIN = GPIO_NUM_25; + /** * @brief The default Clock pin. */ static const gpio_num_t DEFAULT_CLK_PIN = GPIO_NUM_26; + /** + * @brief The default Clock speed. + */ + static const uint32_t DEFAULT_CLK_SPEED = 100000; + I2C(); void beginTransaction(); void endTransaction(); uint8_t getAddress() const; - void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN); + void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0); void read(uint8_t* bytes, size_t length, bool ack=true); void read(uint8_t* byte, bool ack=true); void scan(); diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index ed009f8c..6f4be9b9 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -33,7 +33,7 @@ class JSON { */ class JsonArray { public: - JsonArray(cJSON* node); + int getInt(int item); JsonObject getObject(int item); std::string getString(int item); @@ -47,6 +47,7 @@ class JsonArray { std::string toString(); std::size_t size(); private: + JsonArray(cJSON* node); friend class JSON; friend class JsonObject; /** @@ -61,7 +62,6 @@ class JsonArray { */ class JsonObject { public: - JsonObject(cJSON* node); JsonArray getArray(std::string name); bool getBoolean(std::string name); double getDouble(std::string name); @@ -79,6 +79,7 @@ class JsonObject { std::string toString(); private: + JsonObject(cJSON* node); friend class JSON; friend class JsonArray; /** diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index 21a0b9bc..eb8cd35d 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -8,6 +8,10 @@ #include "System.h" #include +extern "C" { +#include +} + System::System() { // TODO Auto-generated constructor stub @@ -32,7 +36,7 @@ void System::getChipInfo(esp_chip_info_t *info) { * @return The system wide free heap size. */ uint32_t System::getFreeHeapSize() { - return esp_get_free_heap_size(); + return heap_caps_get_free_size(MALLOC_CAP_8BIT); } // getFreeHeapSize diff --git a/mongoose/webserver/main/component.mk b/mongoose/webserver/main/component.mk index b1671d43..186e7eef 100644 --- a/mongoose/webserver/main/component.mk +++ b/mongoose/webserver/main/component.mk @@ -6,5 +6,5 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # -CFLAGS += -DCS_PLATFORM=3 -DMG_DISABLE_DIRECTORY_LISTING=1 -DMG_DISABLE_DAV=1 -DMG_DISABLE_CGI=1 +CFLAGS += -DCS_PLATFORM=15 -DMG_DISABLE_DIRECTORY_LISTING=1 -DMG_DISABLE_DAV=1 -DMG_DISABLE_CGI=1 include $(IDF_PATH)/make/component_common.mk From 371d9f7b8a51c2c88a93f03d59d21144c28bd3df Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 20:25:53 -0600 Subject: [PATCH 061/310] #178 --- networking/bootwifi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/bootwifi/README.md b/networking/bootwifi/README.md index 31129bb8..3d93f557 100644 --- a/networking/bootwifi/README.md +++ b/networking/bootwifi/README.md @@ -9,7 +9,7 @@ What if we take our ESP32 to a new environment where the previously saved access What if we want to change the access point to which the ESP32 connects even if that access point has been previously saved and is still connectable? Simple, the ESP32 can check a GPIO pin at startup and, if that pin is high (default low) then that can be used as a manual indication that we should become an access point without even attempting to connect to the network. -This code is supplied in the form of an ESP-IDF module. +This code is supplied in the form of an ESP-IDF module. In addition, BootWiFi has a pre-requisite of the `cpp_utils` component also distributed as part of this repoistory. The logic uses C++ exception handling and hence C++ exception handling must be enabled in `make menuconfig`. From a7066c3dfd4f9d0643ad8eaec9222f39bc171f0f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 20:36:56 -0600 Subject: [PATCH 062/310] #179 --- cpp_utils/I2C.cpp | 2 +- cpp_utils/WiFi.cpp | 15 +++++++++------ cpp_utils/WiFi.h | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 6f82e6a1..3aa740c7 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -97,7 +97,7 @@ uint8_t I2C::getAddress() const */ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum) { ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d, clockSpeed=%d, portNum=%d", address, sdaPin, sclPin, clockSpeed, portNum); - asser(portNum < I2C_NUM_MAX); + assert(portNum < I2C_NUM_MAX); m_portNum = portNum; m_sdaPin = sdaPin; m_sclPin = sclPin; diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 94a90271..65d717c9 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -187,13 +187,15 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool abort(); } - m_gotIpEvt.take("connectAP"); + m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. + errRc = ::esp_wifi_connect(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } - m_gotIpEvt.wait("connectAP"); + + m_connectFinished.wait("connectAP"); // Wait for the completion of the connection. ESP_LOGD(LOG_TAG, "<< connectAP"); } // connectAP @@ -226,11 +228,12 @@ void WiFi::dump() { // Invoke the event handler. esp_err_t rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); - // If the event we received indicates that we now have an IP address then unlock the mutex that - // indicates we are waiting for an IP. - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { - pWiFi->m_gotIpEvt.give(); + // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that + // indicates we are waiting for a connection complete. + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { + pWiFi->m_connectFinished.give(); } + return rc; } // eventHandler diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 0a980af0..1dcf87fc 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -114,7 +114,7 @@ class WiFi { uint8_t m_dnsCount=0; bool m_eventLoopStarted; bool m_initCalled; - FreeRTOS::Semaphore m_gotIpEvt = FreeRTOS::Semaphore("GotIpEvt"); + FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); public: WiFi(); From 1740cd6dfe6ed17ac4b503674ef09870efb6a3dc Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 20:46:29 -0600 Subject: [PATCH 063/310] #166 --- networking/bootwifi/BootWiFi.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index 531d1e55..f56016b8 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -237,7 +237,15 @@ class BootWifiEventHandler: public WiFiEventHandler { m_pBootWiFi->m_wifi.startAP("Duktape", "Duktape"); ESP_LOGD("BootWifiEventHandler", "<< staDisconnected"); return ESP_OK; - } + } // staDisconnected + + + esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { + ESP_LOGD("BootWifiEventHandler", ">> staGotIP"); + m_pBootWiFi->m_completeSemaphore.give(); // If we got an IP address, then we can end the boot process. + ESP_LOGD("BootWifiEventHandler", "<< staGotIP"); + return ESP_OK; + } // staGotIp private: BootWiFi *m_pBootWiFi; From 571e016f1aa4a0f004d8cc9b36713215d814a104 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 8 Nov 2017 23:00:31 -0600 Subject: [PATCH 064/310] Code changes for #180 --- cpp_utils/System.cpp | 9 ++++++++- cpp_utils/System.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index eb8cd35d..cd15ac13 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -35,7 +35,7 @@ void System::getChipInfo(esp_chip_info_t *info) { * @brief Retrieve the system wide free heap size. * @return The system wide free heap size. */ -uint32_t System::getFreeHeapSize() { +size_t System::getFreeHeapSize() { return heap_caps_get_free_size(MALLOC_CAP_8BIT); } // getFreeHeapSize @@ -50,3 +50,10 @@ std::string System::getIDFVersion() { } // getIDFVersion +/** + * @brief Get the smallest heap size seen. + * @return The smallest heap size seen. + */ +size_t System::getMinimumFreeHeapSize() { + return heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT); +} // getMinimumFreeHeapSize diff --git a/cpp_utils/System.h b/cpp_utils/System.h index 4c084300..915807ef 100644 --- a/cpp_utils/System.h +++ b/cpp_utils/System.h @@ -19,8 +19,9 @@ class System { System(); virtual ~System(); static void getChipInfo(esp_chip_info_t *info); - static uint32_t getFreeHeapSize(); + static size_t getFreeHeapSize(); static std::string getIDFVersion(); + static size_t getMinimumFreeHeapSize(); }; #endif /* COMPONENTS_CPP_UTILS_SYSTEM_H_ */ From 3d17166e1f3f4d6cc0044af5ab9530165f031852 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 9 Nov 2017 10:00:11 +0100 Subject: [PATCH 065/310] Add multi characteristic functionality to resolve issue #175 --- cpp_utils/BLECharacteristic.cpp | 6 +- cpp_utils/BLECharacteristic.h | 4 +- cpp_utils/BLECharacteristicMap.cpp | 8 +- cpp_utils/BLEDescriptor.h | 2 +- cpp_utils/BLEService.cpp | 18 ++-- cpp_utils/BLEService.h | 5 +- .../BLETests/SampleMultiCharacteristics.cpp | 87 +++++++++++++++++++ cpp_utils/tests/BLETests/main.cpp | 6 +- 8 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 18ba65dd..3d3b1d6c 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -95,7 +95,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { esp_attr_control_t control; control.auto_rsp = ESP_GATT_RSP_BY_APP; - m_semaphoreCreateEvt.take("executeCreate"); + //m_semaphoreCreateEvt.take("executeCreate"); /* esp_attr_value_t value; @@ -118,7 +118,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { return; } - m_semaphoreCreateEvt.wait("executeCreate"); + //m_semaphoreCreateEvt.wait("executeCreate"); // Now that we have registered the characteristic, we must also register all the descriptors associated with this // characteristic. We iterate through each of those and invoke the registration call to register them with the @@ -245,7 +245,7 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_ADD_CHAR_EVT: { if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && getService()->getHandle()==param->add_char.service_handle) { - m_semaphoreCreateEvt.give(); + //m_semaphoreCreateEvt.give(); } break; } // ESP_GATTS_ADD_CHAR_EVT diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 24977c00..64891029 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -76,7 +76,9 @@ class BLECharacteristic { void setValue(std::string value); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); + void executeCreate(BLEService* pService); std::string toString(); + uint16_t getHandle(); static const uint32_t PROPERTY_READ = 1<<0; @@ -105,8 +107,6 @@ class BLECharacteristic { esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - void executeCreate(BLEService* pService); - uint16_t getHandle(); esp_gatt_char_prop_t getProperties(); BLEService* getService(); void setHandle(uint16_t handle); diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index bcf4a75d..6efb5980 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -55,8 +55,8 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { * @return The first characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getFirst() { - m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { + m_iterator = m_handleMap.begin(); + if (m_iterator == m_handleMap.end()) { return nullptr; } BLECharacteristic* pRet = m_iterator->second; @@ -70,7 +70,7 @@ BLECharacteristic* BLECharacteristicMap::getFirst() { * @return The next characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_uuidMap.end()) { + if (m_iterator == m_handleMap.end()) { return nullptr; } BLECharacteristic* pRet = m_iterator->second; @@ -90,7 +90,7 @@ void BLECharacteristicMap::handleGATTServerEvent( esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { + for (auto &myPair : m_handleMap) { myPair.second->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 1d32d500..45856f59 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -37,6 +37,7 @@ class BLEDescriptor { void setValue(uint8_t* data, size_t size); void setValue(std::string value); std::string toString(); + uint16_t getHandle(); private: friend class BLEDescriptorMap; @@ -46,7 +47,6 @@ class BLEDescriptor { uint16_t m_handle; BLECharacteristic* m_pCharacteristic; void executeCreate(BLECharacteristic* pCharacteristic); - uint16_t getHandle(); void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); }; diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 669ed261..b45bd326 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -124,7 +124,7 @@ void BLEService::start() { return; } - +/* BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); while(pCharacteristic != nullptr) { @@ -134,7 +134,7 @@ void BLEService::start() { pCharacteristic = m_characteristicMap.getNext(); } // Start each of the characteristics ... these are found in the m_characteristicMap. - +*/ m_semaphoreStartEvt.take("start"); esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); @@ -186,14 +186,18 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { pCharacteristic->getUUID().toString().c_str(), toString().c_str()); + p_createCharacteristic = pCharacteristic; + pCharacteristic->executeCreate(this); // Check that we don't add the same characteristic twice. - if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { +/* if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { ESP_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID"); return; } - - // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID - // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. +*/ + // Remember this characteristic in our map of characteristics. We have mapped characteristic by handle alread, + // now we need to map all characteristics by uuid. + // TODO Now is stored and mapped only one characteristic with unique UUID. We need to change characteristics map + // to store all characteristics, even if there is few characteristics with the same UUID in one service m_characteristicMap.setByUUID(pCharacteristic->getUUID(), pCharacteristic); ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); @@ -245,7 +249,7 @@ void BLEService::handleGATTServerEvent( // for that characteristic. case ESP_GATTS_ADD_CHAR_EVT: { if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = getCharacteristic(BLEUUID(param->add_char.char_uuid)); + BLECharacteristic *pCharacteristic = p_createCharacteristic; if (pCharacteristic == nullptr) { ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", BLEUUID(param->add_char.char_uuid).toString().c_str()); diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 19b472e9..895169ae 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -42,7 +42,7 @@ class BLECharacteristicMap { private: std::map m_uuidMap; std::map m_handleMap; - std::map::iterator m_iterator; + std::map::iterator m_iterator; }; @@ -66,6 +66,7 @@ class BLEService { BLEServer* getServer(); void start(); std::string toString(); + uint16_t getHandle(); private: friend class BLEServer; @@ -85,8 +86,8 @@ class BLEService { FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); uint32_t m_numHandles; + BLECharacteristic* p_createCharacteristic; - uint16_t getHandle(); BLECharacteristic* getLastCreatedCharacteristic(); void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp b/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp new file mode 100644 index 00000000..400e567a --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp @@ -0,0 +1,87 @@ +/** + * Create a new BLE server. + */ +//#include "freertos/FreeRTOS.h" +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include +#include "sdkconfig.h" + +#define SERVICE_UUID_128 "91bad492-b950-4226-aa2b-4ede9fa42f59" +#define CHARACTERISTIC_UUID_128 "0d563a58-196a-48ce-ace2-dfec78acc814" + +static char LOG_TAG[] = "SampleServer"; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + BLEDevice::init(""); + BLEServer* pServer = BLEDevice::createServer(); + + BLEService* pService = pServer->createService(SERVICE_UUID_128); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID_128), + BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setValue("Hello World! Characteristic no 1!"); + + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID_128), + BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->setValue("Hello World! Characteristic no 2!"); + + pCharacteristic = pService->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID_128), + BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->setValue("Hello World! Characteristic no 3!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + /* + * To add 128bit service UUID to advertising REMEMBER: + * BLEDevice::init("ESP32"); in this line + * name should be very short, in most cases no longer than 5 characters. + * In this case we have also setup appearance, so we need to leave device name empty + */ + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + /* + * Setting appearance + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ + pAdvertising->setAppearance(0x1280); + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleMultiCharacteristic(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(4000); + pMainBleServer->start(); + +} // app_main diff --git a/cpp_utils/tests/BLETests/main.cpp b/cpp_utils/tests/BLETests/main.cpp index 732ae16d..bdf60fad 100644 --- a/cpp_utils/tests/BLETests/main.cpp +++ b/cpp_utils/tests/BLETests/main.cpp @@ -20,13 +20,14 @@ void SampleScan(void); void SampleSensorTag(void); void SampleServer(void); void SampleWrite(void); - +void SampleMultiCharacteristic(void); // // Un-comment ONE of the following // --- void app_main(void) { //Sample_MLE_15(); //Sample1(); + SampleMultiCharacteristic(); //SampleClient(); //SampleClient_Notify(); //SampleClientAndServer(); @@ -36,6 +37,7 @@ void app_main(void) { //SampleRead(); //SampleSensorTag(); //SampleScan(); - SampleServer(); + //SampleServer(); //SampleWrite(); } // app_main + From 1d8dfbe41abb361a067974515d60c61c6aaa8cf5 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 9 Nov 2017 23:44:47 -0600 Subject: [PATCH 066/310] Implementation of #185 --- cpp_utils/BLEScan.cpp | 12 ++++++++++++ cpp_utils/BLEScan.h | 1 + 2 files changed, 13 insertions(+) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 176f5a0c..19d44565 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -228,6 +228,17 @@ void BLEScan::stop() { } // stop +/** + * @brief Dump the scan results to the log. + */ +void BLEScanResults::dump() { + ESP_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; i Date: Fri, 10 Nov 2017 19:06:31 +0100 Subject: [PATCH 067/310] Implementation of #184 --- cpp_utils/BLERemoteService.cpp | 39 +++++++++++++++++++++++++++++++++- cpp_utils/BLERemoteService.h | 4 ++++ cpp_utils/HttpServer.cpp | 2 +- cpp_utils/SockServ.cpp | 2 +- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index c0df06c3..ab03292f 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -114,7 +114,7 @@ void BLERemoteService::gattClientEventHandler( } // switch // Send the event to each of the characteristics owned by this service. - for (auto &myPair : m_characteristicMap) { + for (auto &myPair : m_characteristicMapByHandle) { myPair.second->gattClientEventHandler(event, gattc_if, evtParam); } } // gattClientEventHandler @@ -154,6 +154,25 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { return nullptr; } // getCharacteristic +BLERemoteCharacteristic* BLERemoteService::getCharacteristic(uint16_t uuid) { +// Design +// ------ +// We wish to retrieve the characteristic given its handle. It is possible that we have not yet asked the +// device what characteristics it has in which case we have nothing to match against. If we have not +// asked the device about its characteristics, then we do that now. Once we get the results we can then +// examine the characteristics map to see if it has the characteristic we are looking for. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + //std::string v = uuid.toString(); + for (auto &myPair : m_characteristicMapByHandle) { + if (myPair.first == uuid) { + return myPair.second; + } + } + return nullptr; +} // getCharacteristic + /** * @brief Retrieve all the characteristics for this service. @@ -257,6 +276,7 @@ void BLERemoteService::retrieveCharacteristics() { this ); + m_characteristicMapByHandle.insert(std::pair(pNewRemoteCharacteristic->getHandle(), pNewRemoteCharacteristic)); m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); offset++; // Increment our count of number of descriptors found. @@ -283,6 +303,17 @@ std::map* BLERemoteService::getCharacteri return &m_characteristicMap; } // getCharacteristics +void BLERemoteService::getCharacteristics(std::map* ptr) { + ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %#04x", getHandle()); + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %#04x", getHandle()); + *ptr = m_characteristicMapByHandle; +} // getCharacteristics BLEClient* BLERemoteService::getClient() { return m_pClient; @@ -329,6 +360,12 @@ void BLERemoteService::removeCharacteristics() { m_characteristicMap.erase(myPair.first); } m_characteristicMap.clear(); // Clear the map + + for (auto &myPair : m_characteristicMapByHandle) { + delete myPair.second; + m_characteristicMapByHandle.erase(myPair.first); + } + m_characteristicMapByHandle.clear(); // Clear the map } // removeCharacteristics diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 2007fa22..7f8c2f4a 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -32,7 +32,9 @@ class BLERemoteService { // Public methods BLERemoteCharacteristic* getCharacteristic(const char* uuid); BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); + BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); std::map* getCharacteristics(); + void getCharacteristics(std::map* ptr); BLEClient* getClient(void); uint16_t getHandle(); @@ -62,6 +64,8 @@ class BLERemoteService { // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. std::map m_characteristicMap; + // We maintain a map of characteristics owned by this service keyed by a handle. + std::map m_characteristicMapByHandle; bool m_haveCharacteristics; // Have we previously obtained the characteristics. BLEClient* m_pClient; diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 9149ec47..4fd8ceda 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -84,7 +84,7 @@ HttpServer::~HttpServer() { */ class HttpServerTask: public Task { public: - HttpServerTask(std::string name): Task(name, 12*1024) { + HttpServerTask(std::string name): Task(name, 16*1024) { m_pHttpServer = nullptr; }; diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 55416823..e3b67483 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -39,7 +39,7 @@ SockServ::SockServ(uint16_t port) : SockServ() { */ SockServ::SockServ() { m_port = 0; // Unknown port. - m_acceptQueue = xQueueCreate(10, sizeof(Socket)); + m_acceptQueue = xQueueCreate(1, sizeof(Socket)); m_useSSL = false; m_clientSemaphore.take("SockServ"); // Create the queue; deleted in the destructor. } // SockServ From 1a0528e9c3e3dbddfb8442f5f73bf2a6fcaa9fe8 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 12 Nov 2017 22:03:42 -0600 Subject: [PATCH 068/310] #193 --- cpp_utils/BLEDevice.cpp | 3 +- cpp_utils/BLEScan.cpp | 11 ++- cpp_utils/BLEScan.h | 2 +- cpp_utils/BLEUtils.cpp | 10 ++- tools/sdkconfig_compare/README.md | 1 + tools/sdkconfig_compare/sdkconfig_compare.js | 95 ++++++++++++++++++++ 6 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 tools/sdkconfig_compare/README.md create mode 100644 tools/sdkconfig_compare/sdkconfig_compare.js diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 3f1cca2a..0e4887b9 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -152,7 +152,7 @@ void BLEDevice::gapEventHandler( } if (BLEDevice::m_pScan != nullptr) { - BLEDevice::getScan()->gapEventHandler(event, param); + BLEDevice::getScan()->handleGAPEvent(event, param); } } // gapEventHandler @@ -195,6 +195,7 @@ void BLEDevice::init(std::string deviceName) { #ifndef CLASSIC_BT_ENABLED // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 19d44565..7c5b7bd9 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -43,7 +43,7 @@ BLEScan::BLEScan() { * @param [in] event The event type for this event. * @param [in] param Parameter data for this event. */ -void BLEScan::gapEventHandler( +void BLEScan::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { @@ -66,12 +66,21 @@ void BLEScan::gapEventHandler( case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { + // + // ESP_GAP_SEARCH_INQ_CMPL_EVT + // + // Event that indicates that the duration allowed for the search has completed or that we have been + // asked to stop. case ESP_GAP_SEARCH_INQ_CMPL_EVT: { m_stopped = true; m_semaphoreScanEnd.give(); break; } // ESP_GAP_SEARCH_INQ_CMPL_EVT + // + // ESP_GAP_SEARCH_INQ_RES_EVT + // + // Result that has arrived back from a Scan inquiry. case ESP_GAP_SEARCH_INQ_RES_EVT: { if (m_stopped) { // If we are not scanning, nothing to do with the extra results. break; diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index 481ff4c0..edf79a9a 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -57,7 +57,7 @@ class BLEScan { private: BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. friend class BLEDevice; - void gapEventHandler( + void handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 4a08c7c6..da338749 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -634,7 +634,7 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { case BLE_ADDR_TYPE_RPA_RANDOM: return "BLE_ADDR_TYPE_RPA_RANDOM"; default: - return "Unknown esp_ble_addr_type_t"; + return " esp_ble_addr_type_t"; } } // addressTypeToString @@ -700,8 +700,8 @@ const char* BLEUtils::advTypeToString(uint8_t advType) { case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; default: - ESP_LOGD(LOG_TAG, "Unknown adv data type: 0x%x", advType); - return "Unknown"; + ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType); + return ""; } // End switch } // advTypeToString @@ -785,7 +785,7 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { switch(reason) { - case ESP_GATT_CONN_UNKNOWN: + case ESP_GATT_CONN_: return "ESP_GATT_CONN_UNKNOWN"; case ESP_GATT_CONN_L2C_FAILURE: return "ESP_GATT_CONN_L2C_FAILURE"; @@ -1141,6 +1141,8 @@ void BLEUtils::dumpGapEvent( // - ble_adv // - flag // - num_resps + // - adv_data_len + // - scan_rsp_len // case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { diff --git a/tools/sdkconfig_compare/README.md b/tools/sdkconfig_compare/README.md new file mode 100644 index 00000000..78879264 --- /dev/null +++ b/tools/sdkconfig_compare/README.md @@ -0,0 +1 @@ +This is a node.js application which takes as input two files. Each file should be an ESP-IDF `sdkconfig` file. The tool compares the two files and logs the logical distinctions between them. For example, if one file has an entry while the other doesn't, that will be logged. If one file has an entry with a value that is different from the other, that too will be logged. \ No newline at end of file diff --git a/tools/sdkconfig_compare/sdkconfig_compare.js b/tools/sdkconfig_compare/sdkconfig_compare.js new file mode 100644 index 00000000..7ad29ed2 --- /dev/null +++ b/tools/sdkconfig_compare/sdkconfig_compare.js @@ -0,0 +1,95 @@ +// Node.js application for comparing two ESP-IDF configuration files (sdkconfig) +const fs = require("fs"); // Require the file system processing library +const readline = require("readline"); // Require the readline processing library + +// buildMap +// Read the sdkconfig file specified by fileName and produce a map of the name/value pairs contained +// within. A Promise is returned that is fulfilled when the file has been read. +function buildMap(fileName) { + const promise = new Promise(function(resolve, reject) { + var readStream = fs.createReadStream(fileName); + readStream.on("error", (err) => { + reject(err); + }); + const map = {}; + + const lineReader = readline.createInterface({ + input: readStream, + crlfDelay: Infinity + }); + + // Called when a new line has been read from the file. + lineReader.on("line", (line) => { + line = line.trim(); // Trim whitespace from the line. + + if (line.length == 0) { // Ignore empty lines + return; + } + if (line.startsWith("#")) { // Ignore comment lines + return; + } + + const parts = line.split("="); // Split the line into parts separated by the '=' character. + if (map.hasOwnProperty(parts[0])) { + console.log(`Odd ... we found ${parts[0]} twice.`); + } + map[parts[0]] = parts[1]; // Populate the map element. + }); // on(line) + + // Called when all the lines from the file have been consumed. + lineReader.on("close", () => { + resolve(map); + }); // on(close) + + }); + return promise; +} // buildMap + + +const args = process.argv; +if (args.length != 4) { + console.log("Usage: node sdkconfig_compare file1 file2"); + process.exit(); +} +const file1 = args[2]; +const file2 = args[3]; +buildMap(file1).then((result)=> { + buildMap(file2).then((result2) => { + + // Three passes + // In A and not B + // in B and not A + // value different in A and B + for (const prop in result) { + if (result.hasOwnProperty(prop)) { + if (!result2.hasOwnProperty(prop)) { + console.log(`${prop} in ${file1} but not in ${file2}`); + } + } + } + + for (const prop in result2) { + if (result2.hasOwnProperty(prop)) { + if (!result.hasOwnProperty(prop)) { + console.log(`${prop} in ${file2} but not in ${file1}`); + } + } + } + + for (const prop in result) { + if (result.hasOwnProperty(prop)) { + if (result2.hasOwnProperty(prop)) { + if (result[prop] != result2[prop]) { + console.log(`${prop} values different "${result[prop]}" vs "${result2[prop]}"`); + } + } + } + } + }).catch((err) => { + console.log(err); + process.exit(); + }); +}).catch((err) => { + console.log(err); + process.exit(); +}); \ No newline at end of file From 3b27c1ad7501a5e00156e7534730aace13934cb4 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 12 Nov 2017 22:28:10 -0600 Subject: [PATCH 069/310] Emergency path for clean build --- cpp_utils/BLEUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index da338749..a40485bd 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -785,7 +785,7 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { switch(reason) { - case ESP_GATT_CONN_: + case ESP_GATT_CONN_UNKNOWN: return "ESP_GATT_CONN_UNKNOWN"; case ESP_GATT_CONN_L2C_FAILURE: return "ESP_GATT_CONN_L2C_FAILURE"; From 3ffbf70d946717f1f3a49b70bbf5096835e3654a Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 12 Nov 2017 23:21:47 -0600 Subject: [PATCH 070/310] Addition of adFlagsToString #190 --- cpp_utils/BLEUtils.cpp | 29 ++++++++++++++++++++++++++++- cpp_utils/BLEUtils.h | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index a40485bd..0594575f 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -639,6 +639,32 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { } // 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. * @@ -1147,7 +1173,7 @@ void BLEUtils::dumpGapEvent( case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { - ESP_LOGD(LOG_TAG, "search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d, num_resps: %d, adv_data_len: %d, scan_rsp_len: %d", + ESP_LOGD(LOG_TAG, "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), @@ -1155,6 +1181,7 @@ void BLEUtils::dumpGapEvent( 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 diff --git a/cpp_utils/BLEUtils.h b/cpp_utils/BLEUtils.h index 5ef6c378..b2baee53 100644 --- a/cpp_utils/BLEUtils.h +++ b/cpp_utils/BLEUtils.h @@ -21,6 +21,7 @@ class BLEUtils { public: static const char* addressTypeToString(esp_ble_addr_type_t type); + static std::string adFlagsToString(uint8_t adFlags); static const char* advTypeToString(uint8_t advType); static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true); From a7982b4ae0e1e81c476ef0f8d73632dc41724f0f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 13 Nov 2017 19:28:05 -0600 Subject: [PATCH 071/310] Code changes for #197 --- cpp_utils/BLEDevice.cpp | 22 ++++++++++++++++++++++ cpp_utils/BLEDevice.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 0e4887b9..a701459b 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -254,4 +254,26 @@ void BLEDevice::init(std::string deviceName) { } // init +/** + * @brief Set the transmission power. + * The power level can be one of: + * * ESP_PWR_LVL_N14 + * * ESP_PWR_LVL_N11 + * * ESP_PWR_LVL_N8 + * * ESP_PWR_LVL_N5 + * * ESP_PWR_LVL_N2 + * * ESP_PWR_LVL_P1 + * * ESP_PWR_LVL_P4 + * * ESP_PWR_LVL_P7 + * @param [in] powerLevel. + */ +/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { + ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); + esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + }; + ESP_LOGD(LOG_TAG, "<< setPower"); +} // setPower + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index eb9fdaa7..5a09b5c0 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -13,6 +13,7 @@ #include // ESP32 BLE #include // Part of C++ STL #include +#include #include "BLEServer.h" #include "BLEClient.h" @@ -52,6 +53,8 @@ class BLEDevice { esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + static void setPower(esp_power_level_t powerLevel); + }; // class BLE #endif // CONFIG_BT_ENABLED From d72aebcaef860c66724358da2b506af3e8c4e642 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 15 Nov 2017 00:47:02 +0100 Subject: [PATCH 072/310] Revert "Add multiple characteristic functionality to resolve issue #175" --- cpp_utils/BLECharacteristic.cpp | 6 +- cpp_utils/BLECharacteristic.h | 4 +- cpp_utils/BLECharacteristicMap.cpp | 8 +- cpp_utils/BLEDescriptor.h | 2 +- cpp_utils/BLEService.cpp | 18 ++-- cpp_utils/BLEService.h | 5 +- .../BLETests/SampleMultiCharacteristics.cpp | 87 ------------------- cpp_utils/tests/BLETests/main.cpp | 6 +- 8 files changed, 21 insertions(+), 115 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 3d3b1d6c..18ba65dd 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -95,7 +95,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { esp_attr_control_t control; control.auto_rsp = ESP_GATT_RSP_BY_APP; - //m_semaphoreCreateEvt.take("executeCreate"); + m_semaphoreCreateEvt.take("executeCreate"); /* esp_attr_value_t value; @@ -118,7 +118,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { return; } - //m_semaphoreCreateEvt.wait("executeCreate"); + m_semaphoreCreateEvt.wait("executeCreate"); // Now that we have registered the characteristic, we must also register all the descriptors associated with this // characteristic. We iterate through each of those and invoke the registration call to register them with the @@ -245,7 +245,7 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_ADD_CHAR_EVT: { if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && getService()->getHandle()==param->add_char.service_handle) { - //m_semaphoreCreateEvt.give(); + m_semaphoreCreateEvt.give(); } break; } // ESP_GATTS_ADD_CHAR_EVT diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 64891029..24977c00 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -76,9 +76,7 @@ class BLECharacteristic { void setValue(std::string value); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); - void executeCreate(BLEService* pService); std::string toString(); - uint16_t getHandle(); static const uint32_t PROPERTY_READ = 1<<0; @@ -107,6 +105,8 @@ class BLECharacteristic { esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void executeCreate(BLEService* pService); + uint16_t getHandle(); esp_gatt_char_prop_t getProperties(); BLEService* getService(); void setHandle(uint16_t handle); diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index 6efb5980..bcf4a75d 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -55,8 +55,8 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { * @return The first characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getFirst() { - m_iterator = m_handleMap.begin(); - if (m_iterator == m_handleMap.end()) { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { return nullptr; } BLECharacteristic* pRet = m_iterator->second; @@ -70,7 +70,7 @@ BLECharacteristic* BLECharacteristicMap::getFirst() { * @return The next characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_handleMap.end()) { + if (m_iterator == m_uuidMap.end()) { return nullptr; } BLECharacteristic* pRet = m_iterator->second; @@ -90,7 +90,7 @@ void BLECharacteristicMap::handleGATTServerEvent( esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. - for (auto &myPair : m_handleMap) { + for (auto &myPair : m_uuidMap) { myPair.second->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 45856f59..1d32d500 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -37,7 +37,6 @@ class BLEDescriptor { void setValue(uint8_t* data, size_t size); void setValue(std::string value); std::string toString(); - uint16_t getHandle(); private: friend class BLEDescriptorMap; @@ -47,6 +46,7 @@ class BLEDescriptor { uint16_t m_handle; BLECharacteristic* m_pCharacteristic; void executeCreate(BLECharacteristic* pCharacteristic); + uint16_t getHandle(); void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); }; diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index b45bd326..669ed261 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -124,7 +124,7 @@ void BLEService::start() { return; } -/* + BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); while(pCharacteristic != nullptr) { @@ -134,7 +134,7 @@ void BLEService::start() { pCharacteristic = m_characteristicMap.getNext(); } // Start each of the characteristics ... these are found in the m_characteristicMap. -*/ + m_semaphoreStartEvt.take("start"); esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); @@ -186,18 +186,14 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { pCharacteristic->getUUID().toString().c_str(), toString().c_str()); - p_createCharacteristic = pCharacteristic; - pCharacteristic->executeCreate(this); // Check that we don't add the same characteristic twice. -/* if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { ESP_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID"); return; } -*/ - // Remember this characteristic in our map of characteristics. We have mapped characteristic by handle alread, - // now we need to map all characteristics by uuid. - // TODO Now is stored and mapped only one characteristic with unique UUID. We need to change characteristics map - // to store all characteristics, even if there is few characteristics with the same UUID in one service + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. m_characteristicMap.setByUUID(pCharacteristic->getUUID(), pCharacteristic); ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); @@ -249,7 +245,7 @@ void BLEService::handleGATTServerEvent( // for that characteristic. case ESP_GATTS_ADD_CHAR_EVT: { if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = p_createCharacteristic; + BLECharacteristic *pCharacteristic = getCharacteristic(BLEUUID(param->add_char.char_uuid)); if (pCharacteristic == nullptr) { ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", BLEUUID(param->add_char.char_uuid).toString().c_str()); diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 895169ae..19b472e9 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -42,7 +42,7 @@ class BLECharacteristicMap { private: std::map m_uuidMap; std::map m_handleMap; - std::map::iterator m_iterator; + std::map::iterator m_iterator; }; @@ -66,7 +66,6 @@ class BLEService { BLEServer* getServer(); void start(); std::string toString(); - uint16_t getHandle(); private: friend class BLEServer; @@ -86,8 +85,8 @@ class BLEService { FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); uint32_t m_numHandles; - BLECharacteristic* p_createCharacteristic; + uint16_t getHandle(); BLECharacteristic* getLastCreatedCharacteristic(); void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp b/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp deleted file mode 100644 index 400e567a..00000000 --- a/cpp_utils/tests/BLETests/SampleMultiCharacteristics.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Create a new BLE server. - */ -//#include "freertos/FreeRTOS.h" -#include "BLEDevice.h" -#include "BLEServer.h" -#include "BLEUtils.h" -#include "BLE2902.h" -#include -#include -#include -#include "sdkconfig.h" - -#define SERVICE_UUID_128 "91bad492-b950-4226-aa2b-4ede9fa42f59" -#define CHARACTERISTIC_UUID_128 "0d563a58-196a-48ce-ace2-dfec78acc814" - -static char LOG_TAG[] = "SampleServer"; - -class MainBLEServer: public Task { - void run(void *data) { - ESP_LOGD(LOG_TAG, "Starting BLE work!"); - - BLEDevice::init(""); - BLEServer* pServer = BLEDevice::createServer(); - - BLEService* pService = pServer->createService(SERVICE_UUID_128); - - BLECharacteristic* pCharacteristic = pService->createCharacteristic( - BLEUUID(CHARACTERISTIC_UUID_128), - BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_INDICATE - ); - pCharacteristic->setValue("Hello World! Characteristic no 1!"); - - pCharacteristic = pService->createCharacteristic( - BLEUUID(CHARACTERISTIC_UUID_128), - BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_INDICATE - ); - - pCharacteristic->setValue("Hello World! Characteristic no 2!"); - - pCharacteristic = pService->createCharacteristic( - BLEUUID(CHARACTERISTIC_UUID_128), - BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_INDICATE - ); - - pCharacteristic->setValue("Hello World! Characteristic no 3!"); - - BLE2902* p2902Descriptor = new BLE2902(); - p2902Descriptor->setNotifications(true); - pCharacteristic->addDescriptor(p2902Descriptor); - pService->start(); - - BLEAdvertising* pAdvertising = pServer->getAdvertising(); - /* - * To add 128bit service UUID to advertising REMEMBER: - * BLEDevice::init("ESP32"); in this line - * name should be very short, in most cases no longer than 5 characters. - * In this case we have also setup appearance, so we need to leave device name empty - */ - pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); - /* - * Setting appearance - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml - */ - pAdvertising->setAppearance(0x1280); - pAdvertising->start(); - - ESP_LOGD(LOG_TAG, "Advertising started!"); - delay(1000000); - } -}; - - -void SampleMultiCharacteristic(void) -{ - //esp_log_level_set("*", ESP_LOG_DEBUG); - MainBLEServer* pMainBleServer = new MainBLEServer(); - pMainBleServer->setStackSize(4000); - pMainBleServer->start(); - -} // app_main diff --git a/cpp_utils/tests/BLETests/main.cpp b/cpp_utils/tests/BLETests/main.cpp index bdf60fad..732ae16d 100644 --- a/cpp_utils/tests/BLETests/main.cpp +++ b/cpp_utils/tests/BLETests/main.cpp @@ -20,14 +20,13 @@ void SampleScan(void); void SampleSensorTag(void); void SampleServer(void); void SampleWrite(void); -void SampleMultiCharacteristic(void); + // // Un-comment ONE of the following // --- void app_main(void) { //Sample_MLE_15(); //Sample1(); - SampleMultiCharacteristic(); //SampleClient(); //SampleClient_Notify(); //SampleClientAndServer(); @@ -37,7 +36,6 @@ void app_main(void) { //SampleRead(); //SampleSensorTag(); //SampleScan(); - //SampleServer(); + SampleServer(); //SampleWrite(); } // app_main - From d8299d072188f57e03c6250268475d20caa2ba55 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 15 Nov 2017 08:23:29 +0100 Subject: [PATCH 073/310] Multiple characteristics feature #175 --- cpp_utils/BLECharacteristic.cpp | 8 ++-- cpp_utils/BLECharacteristic.h | 2 +- cpp_utils/BLECharacteristicMap.cpp | 18 ++++----- cpp_utils/BLEDescriptor.h | 2 +- cpp_utils/BLERemoteCharacteristic.cpp | 1 + cpp_utils/BLERemoteService.cpp | 53 ++++----------------------- cpp_utils/BLERemoteService.h | 4 +- cpp_utils/BLEService.cpp | 6 +-- cpp_utils/BLEService.h | 10 ++--- 9 files changed, 33 insertions(+), 71 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 18ba65dd..054fb55d 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -244,6 +244,7 @@ void BLECharacteristic::handleGATTServerEvent( // - esp_bt_uuid_t char_uuid case ESP_GATTS_ADD_CHAR_EVT: { if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && + getHandle() == param->add_char.attr_handle && getService()->getHandle()==param->add_char.service_handle) { m_semaphoreCreateEvt.give(); } @@ -418,11 +419,8 @@ void BLECharacteristic::handleGATTServerEvent( // Give each of the descriptors associated with this characteristic the opportunity to handle the // event. - BLEDescriptor *pDescriptor = m_descriptorMap.getFirst(); - while(pDescriptor != nullptr) { - pDescriptor->handleGATTServerEvent(event, gatts_if, param); - pDescriptor = m_descriptorMap.getNext(); - } + + m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); } // handleGATTServerEvent diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 24977c00..cacf1e50 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -77,6 +77,7 @@ class BLECharacteristic { void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); std::string toString(); + uint16_t getHandle(); static const uint32_t PROPERTY_READ = 1<<0; @@ -106,7 +107,6 @@ class BLECharacteristic { esp_ble_gatts_cb_param_t* param); void executeCreate(BLEService* pService); - uint16_t getHandle(); esp_gatt_char_prop_t getProperties(); BLEService* getService(); void setHandle(uint16_t handle); diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index bcf4a75d..86a97441 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -41,8 +41,8 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) { */ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { for (auto &myPair : m_uuidMap) { - if (myPair.second->getUUID().equals(uuid)) { - return myPair.second; + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; } } //return m_uuidMap.at(uuid.toString()); @@ -59,7 +59,7 @@ BLECharacteristic* BLECharacteristicMap::getFirst() { if (m_iterator == m_uuidMap.end()) { return nullptr; } - BLECharacteristic* pRet = m_iterator->second; + BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; } // getFirst @@ -73,7 +73,7 @@ BLECharacteristic* BLECharacteristicMap::getNext() { if (m_iterator == m_uuidMap.end()) { return nullptr; } - BLECharacteristic* pRet = m_iterator->second; + BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; } // getNext @@ -91,7 +91,7 @@ void BLECharacteristicMap::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); + myPair.first->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent @@ -115,9 +115,9 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, * @return N/A. */ void BLECharacteristicMap::setByUUID( - BLEUUID uuid, - BLECharacteristic *pCharacteristic) { - m_uuidMap.insert(std::pair(uuid.toString(), pCharacteristic)); + BLECharacteristic *pCharacteristic, + BLEUUID uuid) { + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); } // setByUUID @@ -134,7 +134,7 @@ std::string BLECharacteristicMap::toString() { stringStream << "\n"; } count++; - stringStream << "handle: 0x" << std::setw(2) << myPair.second->getHandle() << ", uuid: " + myPair.second->getUUID().toString(); + stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); } return stringStream.str(); } // toString diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 1d32d500..45856f59 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -37,6 +37,7 @@ class BLEDescriptor { void setValue(uint8_t* data, size_t size); void setValue(std::string value); std::string toString(); + uint16_t getHandle(); private: friend class BLEDescriptorMap; @@ -46,7 +47,6 @@ class BLEDescriptor { uint16_t m_handle; BLECharacteristic* m_pCharacteristic; void executeCreate(BLECharacteristic* pCharacteristic); - uint16_t getHandle(); void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); }; diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index ba781d29..64b0a92d 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -311,6 +311,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { if (count == 0) { break; } + ESP_LOGE(LOG_TAG, ""); ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); // We now have a new characteristic ... let us add that to our set of known characteristics diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index ab03292f..5227964e 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -114,8 +114,8 @@ void BLERemoteService::gattClientEventHandler( } // switch // Send the event to each of the characteristics owned by this service. - for (auto &myPair : m_characteristicMapByHandle) { - myPair.second->gattClientEventHandler(event, gattc_if, evtParam); + for (auto &myPair : m_characteristicMap) { + myPair.first->gattClientEventHandler(event, gattc_if, evtParam); } } // gattClientEventHandler @@ -147,27 +147,8 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { } std::string v = uuid.toString(); for (auto &myPair : m_characteristicMap) { - if (myPair.first == v) { - return myPair.second; - } - } - return nullptr; -} // getCharacteristic - -BLERemoteCharacteristic* BLERemoteService::getCharacteristic(uint16_t uuid) { -// Design -// ------ -// We wish to retrieve the characteristic given its handle. It is possible that we have not yet asked the -// device what characteristics it has in which case we have nothing to match against. If we have not -// asked the device about its characteristics, then we do that now. Once we get the results we can then -// examine the characteristics map to see if it has the characteristic we are looking for. - if (!m_haveCharacteristics) { - retrieveCharacteristics(); - } - //std::string v = uuid.toString(); - for (auto &myPair : m_characteristicMapByHandle) { - if (myPair.first == uuid) { - return myPair.second; + if (myPair.second == v) { + return myPair.first; } } return nullptr; @@ -276,8 +257,7 @@ void BLERemoteService::retrieveCharacteristics() { this ); - m_characteristicMapByHandle.insert(std::pair(pNewRemoteCharacteristic->getHandle(), pNewRemoteCharacteristic)); - m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); + m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic, pNewRemoteCharacteristic->getUUID().toString())); offset++; // Increment our count of number of descriptors found. } // Loop forever (until we break inside the loop). @@ -291,7 +271,7 @@ void BLERemoteService::retrieveCharacteristics() { * @brief Retrieve a map of all the characteristics of this service. * @return A map of all the characteristics of this service. */ -std::map* BLERemoteService::getCharacteristics() { +std::map* BLERemoteService::getCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); // If is possible that we have not read the characteristics associated with the service so do that // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking @@ -303,17 +283,6 @@ std::map* BLERemoteService::getCharacteri return &m_characteristicMap; } // getCharacteristics -void BLERemoteService::getCharacteristics(std::map* ptr) { - ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %#04x", getHandle()); - // If is possible that we have not read the characteristics associated with the service so do that - // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking - // call and does not return until all the characteristics are available. - if (!m_haveCharacteristics) { - retrieveCharacteristics(); - } - ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %#04x", getHandle()); - *ptr = m_characteristicMapByHandle; -} // getCharacteristics BLEClient* BLERemoteService::getClient() { return m_pClient; @@ -356,16 +325,10 @@ BLEUUID BLERemoteService::getUUID() { */ void BLERemoteService::removeCharacteristics() { for (auto &myPair : m_characteristicMap) { - delete myPair.second; + delete myPair.first; m_characteristicMap.erase(myPair.first); } m_characteristicMap.clear(); // Clear the map - - for (auto &myPair : m_characteristicMapByHandle) { - delete myPair.second; - m_characteristicMapByHandle.erase(myPair.first); - } - m_characteristicMapByHandle.clear(); // Clear the map } // removeCharacteristics @@ -380,7 +343,7 @@ std::string BLERemoteService::toString() { ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle << ", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle; for (auto &myPair : m_characteristicMap) { - ss << "\n" << myPair.second->toString(); + ss << "\n" << myPair.first->toString(); // myPair.second is the value } return ss.str(); diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 7f8c2f4a..521effce 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -33,7 +33,7 @@ class BLERemoteService { BLERemoteCharacteristic* getCharacteristic(const char* uuid); BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); - std::map* getCharacteristics(); + std::map* getCharacteristics(); void getCharacteristics(std::map* ptr); BLEClient* getClient(void); @@ -63,7 +63,7 @@ class BLERemoteService { // Properties // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. - std::map m_characteristicMap; + std::map m_characteristicMap; // We maintain a map of characteristics owned by this service keyed by a handle. std::map m_characteristicMapByHandle; diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 669ed261..b330a1d2 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -189,12 +189,12 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // Check that we don't add the same characteristic twice. if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { ESP_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID"); - return; + //return; } // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. - m_characteristicMap.setByUUID(pCharacteristic->getUUID(), pCharacteristic); + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); } // addCharacteristic @@ -245,7 +245,7 @@ void BLEService::handleGATTServerEvent( // for that characteristic. case ESP_GATTS_ADD_CHAR_EVT: { if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = getCharacteristic(BLEUUID(param->add_char.char_uuid)); + BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); if (pCharacteristic == nullptr) { ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", BLEUUID(param->add_char.char_uuid).toString().c_str()); diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 19b472e9..b37092f2 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -24,8 +24,8 @@ class BLEServer; */ class BLECharacteristicMap { public: - void setByUUID(const char* uuid, BLECharacteristic* pCharacteristic); - void setByUUID(BLEUUID uuid, BLECharacteristic* pCharacteristic); + void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); BLECharacteristic* getByUUID(const char* uuid); BLECharacteristic* getByUUID(BLEUUID uuid); @@ -40,9 +40,9 @@ class BLECharacteristicMap { private: - std::map m_uuidMap; + std::map m_uuidMap; std::map m_handleMap; - std::map::iterator m_iterator; + std::map::iterator m_iterator; }; @@ -66,6 +66,7 @@ class BLEService { BLEServer* getServer(); void start(); std::string toString(); + uint16_t getHandle(); private: friend class BLEServer; @@ -86,7 +87,6 @@ class BLEService { uint32_t m_numHandles; - uint16_t getHandle(); BLECharacteristic* getLastCreatedCharacteristic(); void handleGATTServerEvent( esp_gatts_cb_event_t event, From 2631b4cee30eb90e47645177e8ca0c15d8475673 Mon Sep 17 00:00:00 2001 From: chegewara Date: Fri, 17 Nov 2017 16:55:22 +0100 Subject: [PATCH 074/310] Update BLEDevice.h --- cpp_utils/BLEDevice.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 5a09b5c0..acf20f35 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -53,6 +53,7 @@ class BLEDevice { esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); +public: static void setPower(esp_power_level_t powerLevel); }; // class BLE From 19398e6e42a4d664f7a779477f4978624b7e1da6 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 18 Nov 2017 16:46:17 -0600 Subject: [PATCH 075/310] Addition of missing arduino debugs #210 --- Documentation/images/arduino_debug.png | Bin 0 -> 101871 bytes cpp_utils/ArduinoBLE.md | 19 +--- cpp_utils/BLEAdvertisedDevice.cpp | 10 +- cpp_utils/BLEService.cpp | 4 + cpp_utils/BLEUUID.cpp | 3 + cpp_utils/BLEUtils.cpp | 130 ++++++++++++++++++++----- 6 files changed, 120 insertions(+), 46 deletions(-) create mode 100755 Documentation/images/arduino_debug.png diff --git a/Documentation/images/arduino_debug.png b/Documentation/images/arduino_debug.png new file mode 100755 index 0000000000000000000000000000000000000000..0cccfb5fafe2403ed813e076b1dc1727d1fde221 GIT binary patch literal 101871 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 LL+-PTdC30(dvuwf literal 0 HcmV?d00001 diff --git a/cpp_utils/ArduinoBLE.md b/cpp_utils/ArduinoBLE.md index 133292e8..59772357 100644 --- a/cpp_utils/ArduinoBLE.md +++ b/cpp_utils/ArduinoBLE.md @@ -32,25 +32,10 @@ From October 2017 onwards, a build of the BLE libraries is supplied with the Ard 4. Extract the `ESP32_BLE.zip` file there ## Switching on debugging -The BLE support contains extensive internal diagnostics which can be switched on by editing the file called `sdkconfig.h` and finding the lines which read: +The BLE support contains extensive internal diagnostics which can be switched on through the "Tools > Core Debug Level" setting: -``` -#define CONFIG_LOG_DEFAULT_LEVEL 1 -``` +![](../../Documentation/images/arduino_debug.png) -Change this to: - -``` -#define CONFIG_LOG_DEFAULT_LEVEL 5 -``` - -and rebuild/deploy your project. - -This file can be found in your Arduino IDE installation directories at: - -``` -/hardware/espressif/esp32/tools/sdk/include/config -``` ## Decoding an exception stack trace While using the BLE C++ classes there is always the unfortunate possibility that something will go wrong and your application crash. Fortunately, this results in some debug information being logged to the console. This is known as a *stack trace*. Included in the stack trace are a sequence of hexadecimal numbers known as the *back trace* which are the list of addresses of functions that were executed just before the crash was detected. If we could decode these we would have a lot of great information that could be used to aid in the resolution. Fortunately there is a fantastic project that makes decoding this information very easy indeed. diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index a332c7e2..4347526f 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -209,7 +209,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { payload++; // Skip to type sizeConsumed += 1 + length; // increase the size consumed. - if (length != 0) { // A length of 0 indicate that we have reached the end. + if (length != 0) { // A length of 0 indicates that we have reached the end. ad_type = *payload; payload++; length--; @@ -253,7 +253,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 for (int var = 0; var < length/4; ++var) { - setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4))); + setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4))); } break; } // ESP_BLE_AD_TYPE_32SRV_PART @@ -362,13 +362,15 @@ void BLEAdvertisedDevice::setScan(BLEScan* pScan) { m_pScan = pScan; } // setScan + /** * @brief Set the Service UUID for this device. * @param [in] serviceUUID The discovered serviceUUID */ void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { return setServiceUUID(BLEUUID(serviceUUID)); -} // setRSSI +} // setServiceUUID + /** * @brief Set the Service UUID for this device. @@ -378,7 +380,7 @@ void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) { m_serviceUUIDs.push_back(serviceUUID); m_haveServiceUUID = true; ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); -} // setRSSI +} // setServiceUUID /** diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index b330a1d2..32bcc570 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -22,6 +22,10 @@ #include "BLEUtils.h" #include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + #define NULL_HANDLE (0xffff) static const char* LOG_TAG = "BLEService"; // Tag for logging. diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 6b2311a7..f5c6ceee 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -14,6 +14,9 @@ #include "BLEUUID.h" static const char* LOG_TAG = "BLEUUID"; +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif /** * @brief Copy memory from source to target but in reverse order. diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 0594575f..5d1dec99 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -24,6 +24,10 @@ #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + static const char* LOG_TAG = "BLEUtils"; // Tag for logging. /* @@ -675,56 +679,81 @@ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { */ const char* BLEUtils::advTypeToString(uint8_t advType) { switch(advType) { - case ESP_BLE_AD_TYPE_FLAG: + case ESP_BLE_AD_TYPE_FLAG: // 0x01 return "ESP_BLE_AD_TYPE_FLAG"; - case ESP_BLE_AD_TYPE_16SRV_PART: + + case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 return "ESP_BLE_AD_TYPE_16SRV_PART"; - case ESP_BLE_AD_TYPE_16SRV_CMPL: + + case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 return "ESP_BLE_AD_TYPE_16SRV_CMPL"; - case ESP_BLE_AD_TYPE_32SRV_PART: + + case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 return "ESP_BLE_AD_TYPE_32SRV_PART"; - case ESP_BLE_AD_TYPE_32SRV_CMPL: + + case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 return "ESP_BLE_AD_TYPE_32SRV_CMPL"; - case ESP_BLE_AD_TYPE_128SRV_PART: + + case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 return "ESP_BLE_AD_TYPE_128SRV_PART"; - case ESP_BLE_AD_TYPE_128SRV_CMPL: + + case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 return "ESP_BLE_AD_TYPE_128SRV_CMPL"; - case ESP_BLE_AD_TYPE_NAME_SHORT: + + case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 return "ESP_BLE_AD_TYPE_NAME_SHORT"; - case ESP_BLE_AD_TYPE_NAME_CMPL: + + case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 return "ESP_BLE_AD_TYPE_NAME_CMPL"; - case ESP_BLE_AD_TYPE_TX_PWR: + + case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a return "ESP_BLE_AD_TYPE_TX_PWR"; - case ESP_BLE_AD_TYPE_DEV_CLASS: + + case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b return "ESP_BLE_AD_TYPE_DEV_CLASS"; - case ESP_BLE_AD_TYPE_SM_TK: + + case ESP_BLE_AD_TYPE_SM_TK: // 0x10 return "ESP_BLE_AD_TYPE_SM_TK"; - case ESP_BLE_AD_TYPE_SM_OOB_FLAG: + + case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 return "ESP_BLE_AD_TYPE_SM_OOB_FLAG"; - case ESP_BLE_AD_TYPE_INT_RANGE: + + case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 return "ESP_BLE_AD_TYPE_INT_RANGE"; - case ESP_BLE_AD_TYPE_SOL_SRV_UUID: + + 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: + + case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15 return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID"; - case ESP_BLE_AD_TYPE_SERVICE_DATA: + + case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 return "ESP_BLE_AD_TYPE_SERVICE_DATA"; - case ESP_BLE_AD_TYPE_PUBLIC_TARGET: + + case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 return "ESP_BLE_AD_TYPE_PUBLIC_TARGET"; - case ESP_BLE_AD_TYPE_RANDOM_TARGET: - return "ESP_BLE_Amap1D_TYPE_RANDOM_TARGET"; - case ESP_BLE_AD_TYPE_APPEARANCE: + + 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: + + 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: + + case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 return "ESP_BLE_AD_TYPE_32SERVICE_DATA"; - case ESP_BLE_AD_TYPE_128SERVICE_DATA: + + case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; - case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: + + case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; + default: ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType); return ""; @@ -935,52 +964,76 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType switch(eventType) { 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"; + } return "Unknown"; } // gattServerEventTypeToString @@ -1788,59 +1841,86 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { switch(eventType) { 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"; + default: ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; From 874a0d3559b65d93d75386dc25aba200f9dd8516 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 19 Nov 2017 09:21:22 -0600 Subject: [PATCH 076/310] Implementation of #211 --- cpp_utils/BLECharacteristic.cpp | 23 ++++++ cpp_utils/BLECharacteristicCallbacks.cpp | 37 ---------- cpp_utils/BLEDescriptor.cpp | 92 +++++++++++++++++++----- cpp_utils/BLEDescriptor.h | 19 ++++- cpp_utils/DesignNotes/BLECPP.md | 0 cpp_utils/FreeRTOS.cpp | 48 +++++++++++++ cpp_utils/FreeRTOS.h | 23 +++++- 7 files changed, 184 insertions(+), 58 deletions(-) delete mode 100644 cpp_utils/BLECharacteristicCallbacks.cpp create mode 100644 cpp_utils/DesignNotes/BLECPP.md diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 054fb55d..6d746cf2 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -554,7 +554,9 @@ void BLECharacteristic::setBroadcastProperty(bool value) { * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. */ void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks); m_pCallbacks = pCallbacks; + ESP_LOGD(LOG_TAG, "<< setCallbacks"); } // setCallbacks @@ -693,4 +695,25 @@ std::string BLECharacteristic::toString() { return stringstream.str(); } // toString +BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { + ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); + ESP_LOGD("BLECharacteristicCallbacks", "<< onRead"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) { + ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); + ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite"); +} // onWrite + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLECharacteristicCallbacks.cpp b/cpp_utils/BLECharacteristicCallbacks.cpp deleted file mode 100644 index 46905b51..00000000 --- a/cpp_utils/BLECharacteristicCallbacks.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * BLECharacteristicCallbacks.cpp - * - * Created on: Jul 2, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include "BLECharacteristic.h" -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif -static const char* LOG_TAG = "BLECharacteristicCallbacks"; - - -BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} - -/** - * @brief Callback function to support a read request. - * @param [in] pCharacteristic The characteristic that is the source of the event. - */ -void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { - ESP_LOGD(LOG_TAG, ">> onRead: default"); - ESP_LOGD(LOG_TAG, "<< onRead"); -} // onRead - - -/** - * @brief Callback function to support a write request. - * @param [in] pCharacteristic The characteristic that is the source of the event. - */ -void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) { - ESP_LOGD(LOG_TAG, ">> onWrite: default"); - ESP_LOGD(LOG_TAG, "<< onWrite"); -} // onWrite -#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 8362af0a..054ef420 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -41,7 +41,8 @@ BLEDescriptor::BLEDescriptor(BLEUUID uuid) { m_value.attr_len = 0; m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; m_handle = NULL_HANDLE; - m_pCharacteristic = nullptr; // No initial characteristic. + m_pCharacteristic = nullptr; // No initial characteristic. + m_pCallback = nullptr; // No initial callback. } // BLEDescriptor @@ -177,19 +178,30 @@ void BLEDescriptor::handleGATTServerEvent( // - uint8_t *value case ESP_GATTS_WRITE_EVT: { if (param->write.handle == m_handle) { - setValue(param->write.value, param->write.len); - esp_gatt_rsp_t rsp; + setValue(param->write.value, param->write.len); // Set the value of the descriptor. + + esp_gatt_rsp_t rsp; // Build a response. rsp.attr_value.len = getLength(); rsp.attr_value.handle = m_handle; rsp.attr_value.offset = 0; rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, &rsp); - if (errRc != ESP_OK) { + gatts_if, + param->write.conn_id, + param->write.trans_id, + ESP_GATT_OK, + &rsp); + + if (errRc != ESP_OK) { // Check the return code from the send of the response. ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - } + + if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. + m_pCallback->onWrite(this); // Invoke the onWrite callback handler. + } + } // End of ... this is our handle. + break; } // ESP_GATTS_WRITE_EVT @@ -205,25 +217,36 @@ void BLEDescriptor::handleGATTServerEvent( // - bool need_rsp // case ESP_GATTS_READ_EVT: { - ESP_LOGD(LOG_TAG, "- Testing: Sought handle: 0x%.2x == descriptor handle: 0x%.2x ?", param->read.handle, m_handle); - if (param->read.handle == m_handle) { - ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); - if (param->read.need_rsp) { + if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it + + if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now. + m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. + } + + if (param->read.need_rsp) { // Do we need a response + ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; - rsp.attr_value.len = getLength(); - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.offset = 0; + rsp.attr_value.len = getLength(); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.offset = 0; rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); + esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); - if (errRc != ESP_OK) { + gatts_if, + param->read.conn_id, + param->read.trans_id, + ESP_GATT_OK, + &rsp); + + if (errRc != ESP_OK) { // Check the return code from the send of the response. ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - } - } // ESP_GATTS_READ_EVT + } // End of need a response. + } // End of this is our handle break; } // ESP_GATTS_READ_EVT + default: { break; } @@ -231,6 +254,17 @@ void BLEDescriptor::handleGATTServerEvent( } // handleGATTServerEvent +/** + * @brief Set the callback handlers for this descriptor. + * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. + */ +void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) { + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallback); + m_pCallback = pCallback; + ESP_LOGD(LOG_TAG, "<< setCallbacks"); +} // setCallbacks + + /** * @brief Set the handle of this descriptor. * Set the handle of this descriptor to be the supplied value. @@ -278,4 +312,28 @@ std::string BLEDescriptor::toString() { stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle; return stringstream.str(); } // toString + + +BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) { + ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default"); + ESP_LOGD("BLEDescriptorCallbacks", "<< onRead"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) { + ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default"); + ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite"); +} // onWrite + + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 45856f59..4eda4c91 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -17,6 +17,7 @@ class BLEService; class BLECharacteristic; +class BLEDescriptorCallbacks; /** * @brief A model of a %BLE descriptor. @@ -27,6 +28,7 @@ class BLEDescriptor { BLEDescriptor(BLEUUID uuid); virtual ~BLEDescriptor(); + uint16_t getHandle(); size_t getLength(); BLEUUID getUUID(); uint8_t* getValue(); @@ -34,10 +36,10 @@ class BLEDescriptor { esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void setCallbacks(BLEDescriptorCallbacks* pCallbacks); void setValue(uint8_t* data, size_t size); void setValue(std::string value); std::string toString(); - uint16_t getHandle(); private: friend class BLEDescriptorMap; @@ -46,9 +48,24 @@ class BLEDescriptor { esp_attr_value_t m_value; uint16_t m_handle; BLECharacteristic* m_pCharacteristic; + BLEDescriptorCallbacks* m_pCallback; void executeCreate(BLECharacteristic* pCharacteristic); void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); }; + +/** + * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. + * + * When a server application creates a %BLE descriptor, we may wish to be informed when there is either + * a read or write request to the descriptors value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class BLEDescriptorCallbacks { +public: + virtual ~BLEDescriptorCallbacks(); + virtual void onRead(BLEDescriptor* pDescriptor); + virtual void onWrite(BLEDescriptor* pDescriptor); +}; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md new file mode 100644 index 00000000..e69de29b diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 3a80c698..92390424 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -212,3 +212,51 @@ void FreeRTOS::Semaphore::setName(std::string name) { m_name = name; } // setName + +/** + * @brief Create a ring buffer. + * @param [in] length The amount of storage to allocate for the ring buffer. + * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF. + */ +Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) { + m_handle = ::xRingbufferCreate(length, type); +} // Ringbuffer + + +Ringbuffer::~Ringbuffer() { + ::vRingbufferDelete(m_handle); +} // ~Ringbuffer + + +/** + * @brief Receive data from the buffer. + * @param [out] size On return, the size of data returned. + * @param [in] wait How long to wait. + * @return A pointer to the storage retrieved. + */ +void* Ringbuffer::receive(size_t* size, TickType_t wait) { + return ::xRingbufferReceive(m_handle, size, wait); +} // receive + + +/** + * @brief Return an item. + * @param [in] item The item to be returned/released. + */ +void Ringbuffer::returnItem(void* item) { + ::vRingbufferReturnItem(m_handle, item); +} // returnItem + + +/** + * @brief Send data to the buffer. + * @param [in] data The data to place into the buffer. + * @param [in] length The length of data to place into the buffer. + * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. + * @return + */ +uint32_t Ringbuffer::send(void* data, size_t length, TickType_t wait) { + return ::xRingbufferSend(m_handle, data, length, wait); +} // send + + diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index 30d33be7..43a3b8f4 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -11,9 +11,10 @@ #include #include -#include // Include the base FreeRTOS definitions -#include // Include the task definitions -#include // Include the semaphore definitions +#include // Include the base FreeRTOS definitions. +#include // Include the task definitions. +#include // Include the semaphore definitions. +#include // Include the ringbuffer definitions. /** @@ -50,4 +51,20 @@ class FreeRTOS { }; }; + +/** + * @brief Ringbuffer. + */ +class Ringbuffer { +public: + Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT); + ~Ringbuffer(); + + void* receive(size_t* size, TickType_t wait = portMAX_DELAY); + void returnItem(void* item); + uint32_t send(void* data, size_t length, TickType_t wait = portMAX_DELAY); +private: + RingbufHandle_t m_handle; +}; + #endif /* MAIN_FREERTOS_H_ */ From b53dd77df26a75494406dc63d378443b7a1c56ec Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 19 Nov 2017 10:34:12 -0600 Subject: [PATCH 077/310] Fix for #212 --- cpp_utils/DesignNotes/BLECPP.md | 6 ++++++ cpp_utils/Makefile.arduino | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md index e69de29b..db920dd3 100644 --- a/cpp_utils/DesignNotes/BLECPP.md +++ b/cpp_utils/DesignNotes/BLECPP.md @@ -0,0 +1,6 @@ +# BLE C++ classes + +# BLE Server +## BLE Characteristic callbacks +When a client connects to us as a BLE Server, it can change a characteristics value or read from a characteristic. The BLECharacteristicCallbacks class provides a handler for such. +# BLE Client \ No newline at end of file diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index fc3ccd0f..df7fe54f 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -18,7 +18,6 @@ BLE_FILES= \ BLEAdvertising.h \ BLECharacteristic.cpp \ BLECharacteristic.h \ - BLECharacteristicCallbacks.cpp \ BLECharacteristicMap.cpp \ BLEClient.cpp \ BLEClient.h \ From d6f9485e719405be9a16d364a2177629cf0d9072 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 20 Nov 2017 14:27:15 -0600 Subject: [PATCH 078/310] Fixes for #213 --- cpp_utils/WebServer.cpp | 82 +++++++++++++++++++++++------------- cpp_utils/WebServer.h | 1 + mongoose/webserver/README.md | 2 +- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index ba871243..c250a4d5 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -431,7 +431,36 @@ void WebServer::HTTPResponse::addHeader(const std::string& name, const std::stri void WebServer::HTTPResponse::addHeader(std::string&& name, std::string&& value) { m_headers[std::move(name)] = std::move(value); -} +} // addHeader + + +/** + * @brief Build a string representation of the headers. + * @return A string representation of the headers. + */ +std::string WebServer::HTTPResponse::buildHeaders() { + std::string headers; + unsigned long headers_len = 0; + + for(auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { + if(iter != m_headers.begin()) + headers_len += 2; + headers_len += iter->first.length(); + headers_len += 2; + headers_len += iter->second.length(); + } + headers_len += 1; + headers.resize(headers_len); // Will not have to resize and recopy during the next loop, we have 2 loops but it still ends up being faster + + for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { + if(iter != m_headers.begin()) + headers += "\r\n"; + headers += iter->first; + headers += ": "; + headers += iter->second; + } + return headers; +} // buildHeaders /** * @brief Send data to the HTTP caller. @@ -444,6 +473,9 @@ void WebServer::HTTPResponse::sendData(const std::string& data) { } // sendData + + + /** * @brief Send data to the HTTP caller. * Send the data to the HTTP caller. No further data should be sent after this call. @@ -458,56 +490,48 @@ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { } m_dataSent = true; - std::string headers; - unsigned long headers_len = 0; - - for(auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers_len += 2; - headers_len += iter->first.length(); - headers_len += 2; - headers_len += iter->second.length(); - } - headers_len += 1; - headers.resize(headers_len); // Will not have to resize and recopy during the next loop, we have 2 loops but it still ends up being faster - - for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers += "\r\n"; - headers += iter->first; - headers += ": "; - headers += iter->second; - } - mg_send_head(m_nc, m_status, length, headers.c_str()); + mg_send_head(m_nc, m_status, length, buildHeaders().c_str()); mg_send(m_nc, pData, length); m_nc->flags |= MG_F_SEND_AND_CLOSE; } // sendData + +/** + * + */ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { sendData((uint8_t*) pData, length); } // sendData + +/** + * + */ void WebServer::HTTPResponse::sendChunkHead() { if(m_dataSent) { ESP_LOGE(tag, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); } m_dataSent = true; - mg_send_head(m_nc, m_status, -1, m_headers.c_str()); -} + mg_send_head(m_nc, m_status, -1, buildHeaders().c_str()); +} // sendChunkHead + +/** + * + */ void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { mg_send_http_chunk(m_nc, pData, length); } // sendChunkHead + +/** + * + */ void WebServer::HTTPResponse::closeConnection() { m_nc->flags |= MG_F_SEND_AND_CLOSE; -} +} // closeConnection -void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { - sendData((uint8_t*) pData, length); -} - /** * @brief Set the headers to be sent in the HTTP response. * @param [in] headers The complete set of headers to send to the caller. diff --git a/cpp_utils/WebServer.h b/cpp_utils/WebServer.h index ef879f91..ae06647b 100644 --- a/cpp_utils/WebServer.h +++ b/cpp_utils/WebServer.h @@ -71,6 +71,7 @@ class WebServer { int m_status; std::map m_headers; bool m_dataSent; + std::string buildHeaders(); }; // HTTPResponse /** diff --git a/mongoose/webserver/README.md b/mongoose/webserver/README.md index 4703176a..07e1ef3b 100644 --- a/mongoose/webserver/README.md +++ b/mongoose/webserver/README.md @@ -1,4 +1,4 @@ -#mongoose/webserver +# mongoose/webserver This snippet provides a simple Webserver using the Mongoose library on the ESP32. It provides two URL endpoints: From 2e08a22d993bd82b4c5eefc4a431386f90238cac Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 21 Nov 2017 00:02:34 -0600 Subject: [PATCH 079/310] Fixes for #216 --- cpp_utils/BLEClient.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index ba858287..83e778dc 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -163,8 +163,10 @@ void BLEClient::gattClientEventHandler( if (m_pClientCallbacks != nullptr) { m_pClientCallbacks->onConnect(this); } - m_isConnected = true; // Flag us as connected. - m_semaphoreOpenEvt.give(); + if (evtParam->open.status == ESP_GATT_OK) { + m_isConnected = true; // Flag us as connected. + } + m_semaphoreOpenEvt.give(evtParam->open.status); break; } // ESP_GATTC_OPEN_EVT From 17fbacc3413729cc4c87cd5dfe8d6d208f97d89c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 24 Nov 2017 21:28:55 -0600 Subject: [PATCH 080/310] Code changes for #221 --- cpp_utils/WiFi.cpp | 14 +++++++++++--- cpp_utils/WiFi.h | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 65d717c9..4054dfe5 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -52,9 +52,10 @@ WiFi::WiFi() , netmask(0) , m_pWifiEventHandler(nullptr) { - m_eventLoopStarted = false; - m_initCalled = false; + m_eventLoopStarted = false; + m_initCalled = false; m_pWifiEventHandler = new WiFiEventHandler(); + m_apConnected = false; // Are we connected to an access point? } // WiFi /** @@ -150,9 +151,10 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @param [in] waitForConnection Block until the connection has an outcome. * @return N/A. */ -void WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ +bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ ESP_LOGD(LOG_TAG, ">> connectAP"); + m_apConnected = false; init(); if (ip != 0 && gw != 0 && netmask != 0) { @@ -197,6 +199,7 @@ void WiFi::connectAP(const std::string& ssid, const std::string& password, bool m_connectFinished.wait("connectAP"); // Wait for the completion of the connection. ESP_LOGD(LOG_TAG, "<< connectAP"); + return m_apConnected; // Return true if we are now connected and false if not. } // connectAP @@ -231,6 +234,11 @@ void WiFi::dump() { // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that // indicates we are waiting for a connection complete. if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the flag. + pWiFi->m_apConnected = true; + } else { + pWiFi->m_apConnected = false; + } pWiFi->m_connectFinished.give(); } diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 1dcf87fc..e9af3d67 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -114,6 +114,7 @@ class WiFi { uint8_t m_dnsCount=0; bool m_eventLoopStarted; bool m_initCalled; + bool m_apConnected; // Are we connected to an access point? FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); public: @@ -127,7 +128,7 @@ class WiFi { void setDNSServer(int numdns, ip_addr_t ip); struct in_addr getHostByName(const std::string& hostName); struct in_addr getHostByName(const char* hostName); - void connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); + bool connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); void dump(); static std::string getApMac(); static tcpip_adapter_ip_info_t getApIpInfo(); From 5fafd4443c0ba81df1a1a607fb89f204eefa6aa7 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 25 Nov 2017 13:25:56 -0600 Subject: [PATCH 081/310] base line for #226 --- cpp_utils/ArduinoBLE.md | 8 +- cpp_utils/GeneralUtils.cpp | 7 +- cpp_utils/GeneralUtils.h | 2 +- cpp_utils/HttpServer.cpp | 12 +- cpp_utils/Memory.cpp | 86 ++++++- cpp_utils/Memory.h | 2 + cpp_utils/SockServ.cpp | 1 + cpp_utils/Socket.cpp | 7 +- cpp_utils/System.cpp | 7 + cpp_utils/System.h | 1 + mongoose/webserver/sdkconfig | 463 ++++++++++++++++++++++++++++++----- 11 files changed, 510 insertions(+), 86 deletions(-) diff --git a/cpp_utils/ArduinoBLE.md b/cpp_utils/ArduinoBLE.md index 59772357..d7a446af 100644 --- a/cpp_utils/ArduinoBLE.md +++ b/cpp_utils/ArduinoBLE.md @@ -1,11 +1,13 @@ # Arduino BLE Support -As part of this Github project, we provide libraries for Bluetooth Low Energy (BLE) for the ESP32 Arduino environment. Support for this capability is still in the process of being cooked (as of September 2017). As such you should not build product based on these functions as changes to the API and implementation are extremely likely over the next couple of months. +As part of this Github project, we provide libraries for Bluetooth Low Energy (BLE) for the ESP32 Arduino environment. Support for this capability is still in the process of being cooked (as of November 2017). As such you should not build product based on these functions as changes to the API and implementation are extremely likely over the next couple of months. That said, we now have the ability to produce a driver you can use for testing. This will give you early access to the function while give those who choose to build and maintain the code early feedback on what works, what doesn't and what can be improved from a usage perspective. When complete, the BLE support for ESP32 Arduino will be available as a single ZIP file. The file will be called **ESP32_BLE.zip**. It is this file that will be able to be imported into the Arduino IDE from the `Sketch -> Include Library -> Add .ZIP library`. When initial development of the library has been completed, this ZIP will be placed under some form of release control so that an ordinary Arduino IDE user can simply download this as a unit and install. -A build of the BLE support for Arduino can be found through the Arduino IDE. Visit Sketch -> Include Library -> Manage Libraries. In the library filter, enter "esp32 ble arduino". The search will narrow and you should see "ESP32 BLE Arduino" available for installation or upgrade. +Update: As of 2017-11, the BLE support has been included with the Arduino ESP32 base package. + +A build of the BLE support for Arduino can be found through the Arduino IDE. Visit `Sketch -> Include Library -> Manage Libraries`. In the library filter, enter "esp32 ble arduino". The search will narrow and you should see "ESP32 BLE Arduino" available for installation or upgrade. @@ -32,7 +34,7 @@ From October 2017 onwards, a build of the BLE libraries is supplied with the Ard 4. Extract the `ESP32_BLE.zip` file there ## Switching on debugging -The BLE support contains extensive internal diagnostics which can be switched on through the "Tools > Core Debug Level" setting: +The BLE support contains extensive internal diagnostics which can be switched on through the `Tools > Core Debug Level` setting: ![](../../Documentation/images/arduino_debug.png) diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index e8eb3a0a..fd0c740c 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -398,9 +398,4 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { } // errorToString -/** - * @brief Restart the ESP32. - */ -void GeneralUtils::restart() { - esp_restart(); -} // restart + diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 2d55abfc..54aae5e3 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -22,7 +22,7 @@ class GeneralUtils { static const char* errorToString(esp_err_t errCode); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); - static void restart(); + }; #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 4fd8ceda..9ae382b5 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -15,6 +15,7 @@ #include "FileSystem.h" #include "WebSocket.h" #include "GeneralUtils.h" +#include "Memory.h" static const char* LOG_TAG = "HttpServer"; #undef close @@ -65,9 +66,9 @@ static void listDirectory(std::string path, HttpResponse& response) { * Constructor for HTTP Server */ HttpServer::HttpServer() { - m_portNumber = 80; // The default port number. - m_rootPath = ""; // The default path. - m_useSSL = false; // Default SSL is no. + m_portNumber = 80; // The default port number. + m_rootPath = ""; // The default path. + m_useSSL = false; // Default SSL is no. setDirectoryListing(false); // Default directory listing is no. } // HttpServer @@ -188,11 +189,12 @@ class HttpServerTask: public Task { while(1) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); - + Memory::checkIntegrity(); try { clientSocket = m_pHttpServer->m_sockServ.waitForNewClient(); // Block waiting for a new external client connection. } - catch(std::exception e) { + catch(std::exception &e) { + ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); return; } diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp index c5e7028b..e3be59db 100644 --- a/cpp_utils/Memory.cpp +++ b/cpp_utils/Memory.cpp @@ -11,20 +11,36 @@ #include #include "GeneralUtils.h" extern "C" { -#include -#include + #include + #include } #include +#include static const char* LOG_TAG = "Memory"; heap_trace_record_t* Memory::m_pRecords = nullptr; size_t Memory::m_lastHeapSize = 0; + +/** + * @brief Check the integrity of memory. + * @return True if all integrity checks passed + */ +/* STATIC */ bool Memory::checkIntegrity() { + bool rc = ::heap_caps_check_integrity_all(true); + if (rc == false && m_pRecords != nullptr) { + dumpRanges(); + abort(); + } + return rc; +} // checkIntegrity + + /** * @brief Dump the trace records from the heap. */ -void Memory::dump() { +/* STATIC */ void Memory::dump() { ::heap_trace_dump(); } // dump @@ -34,13 +50,53 @@ void Memory::dump() { int diff = currentUsage - m_lastHeapSize; ESP_LOGD(LOG_TAG, "%s: Heap changed by %d bytes (%d to %d)", tag.c_str(), diff, m_lastHeapSize, currentUsage); m_lastHeapSize = currentUsage; -} +} // dumpHeapChange + + +/** + * @brief Dump the ranges of allocations found. + */ +/* STATIC */ void Memory::dumpRanges() { + // Each record contained in the Heap trace has the following format: + // + // * uint32_t ccount – Timestamp of record. + // * void* address – Address that was allocated or released. + // * size_t size – Size of the block that was requested and allocated. + // * void* alloced_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of allocator + // * void* freed_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of releasor + // + if (m_pRecords == nullptr) { + return; + } + esp_log_level_set("*", ESP_LOG_NONE); + size_t count = heap_trace_get_count(); + heap_trace_record_t record; + printf(">>> dumpRanges\n"); + for (size_t i=0; i 0); if (m_pRecords != nullptr) { ESP_LOGE(LOG_TAG, "Already initialized"); @@ -60,7 +116,10 @@ void Memory::init(uint32_t recordCount) { } // init -void Memory::resumeTrace() { +/** + * @brief Resume previously paused trace. + */ +/* STATIC */ void Memory::resumeTrace() { esp_err_t errRc = ::heap_trace_resume(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "heap_trace_resume: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -69,7 +128,10 @@ void Memory::resumeTrace() { } // resumeTrace -void Memory::startTraceAll() { +/** + * @brief Start tracing all allocate and free calls. + */ +/* STATIC */ void Memory::startTraceAll() { esp_err_t errRc = ::heap_trace_start(HEAP_TRACE_ALL); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -78,7 +140,10 @@ void Memory::startTraceAll() { } // startTraceAll -void Memory::startTraceLeaks() { +/** + * Start tracing leaks. Matched allocate and free calls are removed. + */ +/* STATIC */ void Memory::startTraceLeaks() { esp_err_t errRc = ::heap_trace_start(HEAP_TRACE_LEAKS); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -87,7 +152,10 @@ void Memory::startTraceLeaks() { } // startTraceLeaks -void Memory::stopTrace() { +/** + * @brief Stop recording heap trace. + */ +/* STATIC */ void Memory::stopTrace() { esp_err_t errRc = ::heap_trace_stop(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "heap_trace_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); diff --git a/cpp_utils/Memory.h b/cpp_utils/Memory.h index dca5a838..bcca8c6a 100644 --- a/cpp_utils/Memory.h +++ b/cpp_utils/Memory.h @@ -16,7 +16,9 @@ extern "C" { class Memory { public: + static bool checkIntegrity(); static void dump(); + static void dumpRanges(); static void dumpHeapChange(std::string tag); static void init(uint32_t recordCount); static void resumeTrace(); diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index e3b67483..336ddc90 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -64,6 +64,7 @@ SockServ::~SockServ() { SockServ* pSockServ = (SockServ*)data; try { while(1) { + ESP_LOGD(LOG_TAG, "Waiting on accept") Socket tempSock = pSockServ->m_serverSocket.accept(pSockServ->getSSL()); if (!tempSock.isValid()) { continue; diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index 849c3ef1..422d71cc 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -78,7 +78,7 @@ Socket Socket::accept(bool useSSL) { newSocket.m_sslSock.fd = clientSockFD; newSocket.sslHandshake(); ESP_LOGD(LOG_TAG, "DEBUG DEBUG "); - uint8_t x; + uint8_t x; // What is going on here??? newSocket.receive(&x, 1, 0); // FIX FIX FIX } ESP_LOGD(LOG_TAG, "<< accept: sockFd: %d", clientSockFD); @@ -226,7 +226,10 @@ void Socket::getBind(struct sockaddr* pAddr) { ESP_LOGE(LOG_TAG, "getBind: Socket is not initialized."); } socklen_t nameLen = sizeof(struct sockaddr); - ::getsockname(m_sock, pAddr, &nameLen); + int rc = ::getsockname(m_sock, pAddr, &nameLen); + if (rc != 0) { + ESP_LOGE(LOG_TAG, "Error with getsockname in getBind: %s", strerror(errno)); + } } // getBind diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index cd15ac13..1993be4c 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -57,3 +57,10 @@ std::string System::getIDFVersion() { size_t System::getMinimumFreeHeapSize() { return heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT); } // getMinimumFreeHeapSize + +/** + * @brief Restart the ESP32. + */ +void System::restart() { + esp_restart(); +} // restart diff --git a/cpp_utils/System.h b/cpp_utils/System.h index 915807ef..6459509d 100644 --- a/cpp_utils/System.h +++ b/cpp_utils/System.h @@ -22,6 +22,7 @@ class System { static size_t getFreeHeapSize(); static std::string getIDFVersion(); static size_t getMinimumFreeHeapSize(); + static void restart(); }; #endif /* COMPONENTS_CPP_UTILS_SYSTEM_H_ */ diff --git a/mongoose/webserver/sdkconfig b/mongoose/webserver/sdkconfig index 36eff3ff..80a51a3e 100644 --- a/mongoose/webserver/sdkconfig +++ b/mongoose/webserver/sdkconfig @@ -8,150 +8,493 @@ # CONFIG_TOOLPREFIX="xtensa-esp32-elf-" CONFIG_PYTHON="python" +CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y # # Bootloader config # -# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +CONFIG_LOG_BOOTLOADER_LEVEL_NONE= +CONFIG_LOG_BOOTLOADER_LEVEL_ERROR= CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y -# CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO= +CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG= +CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE= CONFIG_LOG_BOOTLOADER_LEVEL=2 +CONFIG_BOOTLOADER_VDDSDIO_BOOST=y + +# +# Security features +# +CONFIG_SECURE_BOOT_ENABLED= +CONFIG_FLASH_ENCRYPTION_ENABLED= # # Serial flasher config # CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" -# CONFIG_ESPTOOLPY_BAUD_115200B is not set -# CONFIG_ESPTOOLPY_BAUD_230400B is not set +CONFIG_ESPTOOLPY_BAUD_115200B= +CONFIG_ESPTOOLPY_BAUD_230400B= CONFIG_ESPTOOLPY_BAUD_921600B=y -# CONFIG_ESPTOOLPY_BAUD_2MB is not set -# CONFIG_ESPTOOLPY_BAUD_OTHER is not set +CONFIG_ESPTOOLPY_BAUD_2MB= +CONFIG_ESPTOOLPY_BAUD_OTHER= CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 CONFIG_ESPTOOLPY_BAUD=921600 CONFIG_ESPTOOLPY_COMPRESSED=y -# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set -# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y -# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_FLASHMODE_QIO= +CONFIG_FLASHMODE_QOUT= +CONFIG_FLASHMODE_DIO=y +CONFIG_FLASHMODE_DOUT= CONFIG_ESPTOOLPY_FLASHMODE="dio" -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_80M= CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_26M= +CONFIG_ESPTOOLPY_FLASHFREQ_20M= CONFIG_ESPTOOLPY_FLASHFREQ="40m" -# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_1MB= CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE_NORESET= +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +CONFIG_ESPTOOLPY_AFTER_NORESET= +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_MONITOR_BAUD_9600B= +CONFIG_MONITOR_BAUD_57600B= +CONFIG_MONITOR_BAUD_115200B=y +CONFIG_MONITOR_BAUD_230400B= +CONFIG_MONITOR_BAUD_921600B= +CONFIG_MONITOR_BAUD_2MB= +CONFIG_MONITOR_BAUD_OTHER= +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 # # Partition Table # CONFIG_PARTITION_TABLE_SINGLE_APP=y -# CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_TWO_OTA= +CONFIG_PARTITION_TABLE_CUSTOM= CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" CONFIG_APP_OFFSET=0x10000 + +# +# Compiler options +# CONFIG_OPTIMIZATION_LEVEL_DEBUG=y -# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_LEVEL_RELEASE= +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +CONFIG_OPTIMIZATION_ASSERTIONS_SILENT= +CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED= +CONFIG_CXX_EXCEPTIONS= # # Component config # + +# +# Application Level Tracing +# +CONFIG_ESP32_APPTRACE_DEST_TRAX= +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_ENABLE= +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y + +# +# FreeRTOS SystemView Tracing +# +CONFIG_AWS_IOT_SDK= + +# +# Bluetooth +# +CONFIG_BT_ENABLED= +CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 CONFIG_BT_RESERVE_DRAM=0 # -# ESP32-specific config +# ESP32-specific # -# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set -# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_80= +CONFIG_ESP32_DEFAULT_CPU_FREQ_160= CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -CONFIG_ESP32_ENABLE_STACK_WIFI=y -# CONFIG_ESP32_ENABLE_STACK_BT is not set -# CONFIG_ESP32_ENABLE_STACK_NONE is not set CONFIG_MEMMAP_SMP=y -# CONFIG_MEMMAP_TRACEMEM is not set +CONFIG_SPIRAM_SUPPORT= +CONFIG_MEMMAP_TRACEMEM= +CONFIG_MEMMAP_TRACEMEM_TWOBANKS= +CONFIG_ESP32_TRAX= CONFIG_TRACEMEM_RESERVE_DRAM=0x0 -CONFIG_WIFI_ENABLED=y +CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH= +CONFIG_ESP32_ENABLE_COREDUMP_TO_UART= +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_ESP32_ENABLE_COREDUMP= +CONFIG_TWO_UNIVERSAL_MAC_ADDRESS= +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_MAIN_TASK_STACK_SIZE=4096 -CONFIG_NEWLIB_STDOUT_ADDCR=y -# CONFIG_ULP_COPROC_ENABLED is not set +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +CONFIG_NEWLIB_NANO_FORMAT= +CONFIG_CONSOLE_UART_DEFAULT=y +CONFIG_CONSOLE_UART_CUSTOM= +CONFIG_CONSOLE_UART_NONE= +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ULP_COPROC_ENABLED= CONFIG_ULP_COPROC_RESERVE_MEM=0 -# CONFIG_ESP32_PANIC_PRINT_HALT is not set +CONFIG_ESP32_PANIC_PRINT_HALT= CONFIG_ESP32_PANIC_PRINT_REBOOT=y -# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set -# CONFIG_ESP32_PANIC_GDBSTUB is not set +CONFIG_ESP32_PANIC_SILENT_REBOOT= +CONFIG_ESP32_PANIC_GDBSTUB= CONFIG_ESP32_DEBUG_OCDAWARE=y CONFIG_INT_WDT=y CONFIG_INT_WDT_TIMEOUT_MS=300 CONFIG_TASK_WDT=y -# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_PANIC= CONFIG_TASK_WDT_TIMEOUT_S=5 -CONFIG_TASK_WDT_CHECK_IDLE_TASK=y -# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_BROWNOUT_DET_LVL_SEL_1= +CONFIG_BROWNOUT_DET_LVL_SEL_2= +CONFIG_BROWNOUT_DET_LVL_SEL_3= +CONFIG_BROWNOUT_DET_LVL_SEL_4= +CONFIG_BROWNOUT_DET_LVL_SEL_5= +CONFIG_BROWNOUT_DET_LVL_SEL_6= +CONFIG_BROWNOUT_DET_LVL_SEL_7= +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_TIME_SYSCALL_USE_RTC= CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y -# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set -# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_TIME_SYSCALL_USE_FRC1= +CONFIG_ESP32_TIME_SYSCALL_USE_NONE= CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL= +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_XTAL_FREQ_40=y +CONFIG_ESP32_XTAL_FREQ_26= +CONFIG_ESP32_XTAL_FREQ_AUTO= +CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_DISABLE_BASIC_ROM_CONSOLE= +CONFIG_NO_BLOBS= + +# +# Wi-Fi +# +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER= +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP32_WIFI_AMPDU_ENABLED=y +CONFIG_ESP32_WIFI_TX_BA_WIN=6 +CONFIG_ESP32_WIFI_RX_BA_WIN=6 +CONFIG_ESP32_WIFI_NVS_ENABLED=y + +# +# PHY +# +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 + +# +# Power Management +# +CONFIG_PM_ENABLE= + +# +# Ethernet +# +CONFIG_DMA_RX_BUF_NUM=10 +CONFIG_DMA_TX_BUF_NUM=10 +CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE= +CONFIG_EMAC_TASK_PRIORITY=20 + +# +# FAT Filesystem support +# +CONFIG_FATFS_CODEPAGE_ASCII=y +CONFIG_FATFS_CODEPAGE_437= +CONFIG_FATFS_CODEPAGE_720= +CONFIG_FATFS_CODEPAGE_737= +CONFIG_FATFS_CODEPAGE_771= +CONFIG_FATFS_CODEPAGE_775= +CONFIG_FATFS_CODEPAGE_850= +CONFIG_FATFS_CODEPAGE_852= +CONFIG_FATFS_CODEPAGE_855= +CONFIG_FATFS_CODEPAGE_857= +CONFIG_FATFS_CODEPAGE_860= +CONFIG_FATFS_CODEPAGE_861= +CONFIG_FATFS_CODEPAGE_862= +CONFIG_FATFS_CODEPAGE_863= +CONFIG_FATFS_CODEPAGE_864= +CONFIG_FATFS_CODEPAGE_865= +CONFIG_FATFS_CODEPAGE_866= +CONFIG_FATFS_CODEPAGE_869= +CONFIG_FATFS_CODEPAGE_932= +CONFIG_FATFS_CODEPAGE_936= +CONFIG_FATFS_CODEPAGE_949= +CONFIG_FATFS_CODEPAGE_950= +CONFIG_FATFS_CODEPAGE=1 +CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y # # FreeRTOS # CONFIG_FREERTOS_UNICORE=y CONFIG_FREERTOS_CORETIMER_0=y -# CONFIG_FREERTOS_CORETIMER_1 is not set -# CONFIG_FREERTOS_CORETIMER_2 is not set +CONFIG_FREERTOS_CORETIMER_1= CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY= +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y -# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -# CONFIG_FREERTOS_ASSERT_DISABLE is not set -CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y -# CONFIG_ENABLE_MEMORY_DEBUG is not set +CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= +CONFIG_FREERTOS_ASSERT_DISABLE= +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 CONFIG_FREERTOS_ISR_STACKSIZE=1536 -# CONFIG_FREERTOS_DEBUG_INTERNALS is not set +CONFIG_FREERTOS_LEGACY_HOOKS= +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_SUPPORT_STATIC_ALLOCATION= +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_USE_TRACE_FACILITY= +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= +CONFIG_FREERTOS_DEBUG_INTERNALS= + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +CONFIG_HEAP_POISONING_LIGHT= +CONFIG_HEAP_POISONING_COMPREHENSIVE= +CONFIG_HEAP_TRACING= + +# +# libsodium +# +CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y # # Log output # -# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set -# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set -# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y CONFIG_LOG_DEFAULT_LEVEL=5 -# CONFIG_LOG_COLORS is not set +CONFIG_LOG_COLORS= # # LWIP # +CONFIG_L2_TO_L3_COPY= CONFIG_LWIP_MAX_SOCKETS=4 -CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX=0 -# CONFIG_LWIP_SO_REUSE is not set +CONFIG_LWIP_SO_REUSE= +CONFIG_LWIP_SO_RCVBUF= CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +CONFIG_LWIP_IP_FRAG= +CONFIG_LWIP_IP_REASSEMBLY= +CONFIG_LWIP_STATS= +CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y +CONFIG_LWIP_AUTOIP= +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=6 +CONFIG_TCP_MSS=1436 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5744 +CONFIG_TCP_WND_DEFAULT=5744 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +CONFIG_TCP_OVERSIZE_MSS=y +CONFIG_TCP_OVERSIZE_QUARTER_MSS= +CONFIG_TCP_OVERSIZE_DISABLE= + +# +# UDP +# +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=2560 +CONFIG_PPP_SUPPORT= + +# +# ICMP +# +CONFIG_LWIP_MULTICAST_PING= +CONFIG_LWIP_BROADCAST_PING= # # mbedTLS # CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 -# CONFIG_MBEDTLS_DEBUG is not set +CONFIG_MBEDTLS_DEBUG= +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_HARDWARE_MPI= +CONFIG_MBEDTLS_HARDWARE_SHA= +CONFIG_MBEDTLS_HAVE_TIME=y +CONFIG_MBEDTLS_HAVE_TIME_DATE= +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +CONFIG_MBEDTLS_TLS_SERVER_ONLY= +CONFIG_MBEDTLS_TLS_CLIENT_ONLY= +CONFIG_MBEDTLS_TLS_DISABLED= +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +CONFIG_MBEDTLS_PSK_MODES= +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_SSL3= +CONFIG_MBEDTLS_SSL_PROTO_TLS1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS= +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +CONFIG_MBEDTLS_CAMELLIA_C= +CONFIG_MBEDTLS_DES_C= +CONFIG_MBEDTLS_RC4_DISABLED=y +CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= +CONFIG_MBEDTLS_RC4_ENABLED= +CONFIG_MBEDTLS_BLOWFISH_C= +CONFIG_MBEDTLS_XTEA_C= +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +CONFIG_MBEDTLS_RIPEMD160_C= + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y + +# +# OpenSSL +# +CONFIG_OPENSSL_DEBUG= +CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +CONFIG_OPENSSL_ASSERT_EXIT= + +# +# PThreads +# +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=2048 # # SPI Flash driver # -# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ENABLE_COUNTERS= +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +CONFIG_SPIFFS_CACHE_STATS= +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +CONFIG_SPIFFS_GC_STATS= +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y + +# +# Debug Configuration +# +CONFIG_SPIFFS_DBG= +CONFIG_SPIFFS_API_DBG= +CONFIG_SPIFFS_GC_DBG= +CONFIG_SPIFFS_CACHE_DBG= +CONFIG_SPIFFS_CHECK_DBG= +CONFIG_SPIFFS_TEST_VISUALISATION= + +# +# tcpip adapter +# +CONFIG_IP_LOST_TIMER_INTERVAL=120 + +# +# Wear Levelling +# +CONFIG_WL_SECTOR_SIZE_512= +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 From 3db35bdbd02b05fa456d5935102221a6ab2301b4 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 25 Nov 2017 17:26:28 -0600 Subject: [PATCH 082/310] Changes for #217 --- cpp_utils/HttpServer.cpp | 11 +++++------ cpp_utils/HttpServer.h | 5 +++-- cpp_utils/SockServ.cpp | 2 +- cpp_utils/Socket.cpp | 12 +++++------- cpp_utils/Socket.h | 4 ++-- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 9ae382b5..298ab0c3 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -181,17 +181,16 @@ class HttpServerTask: public Task { */ void run(void* data) { m_pHttpServer = (HttpServer*)data; // The passed in data is an instance of an HttpServer. - m_pHttpServer->m_sockServ.setPort(m_pHttpServer->m_portNumber); - m_pHttpServer->m_sockServ.setSSL(m_pHttpServer->m_useSSL); - m_pHttpServer->m_sockServ.start(); + m_pHttpServer->m_socket.setSSL(m_pHttpServer->m_useSSL); + m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber); ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort()); Socket clientSocket; while(1) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); - Memory::checkIntegrity(); + //Memory::checkIntegrity(); try { - clientSocket = m_pHttpServer->m_sockServ.waitForNewClient(); // Block waiting for a new external client connection. + clientSocket = m_pHttpServer->m_socket.accept(); // Block waiting for a new external client connection. } catch(std::exception &e) { ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); @@ -353,7 +352,7 @@ void HttpServer::stop() { // that is listening for incoming connections. That will then shutdown all the other // activities. ESP_LOGD(LOG_TAG, ">> stop"); - m_sockServ.stop(); + m_socket.close(); ESP_LOGD(LOG_TAG, "<< stop"); } // stop diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index 4b7e3083..f07abedd 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -16,7 +16,7 @@ #include "SockServ.h" #include "HttpRequest.h" #include "HttpResponse.h" -#include "SockServ.h" +//#include "SockServ.h" #include class HttpServerTask; @@ -89,7 +89,8 @@ class HttpServer { std::string m_rootPath; // Root path into the file system. bool m_useSSL; // Is this server listening on an HTTPS port? bool m_directoryListing; // Should we list directory content? - SockServ m_sockServ; // Server socket. + //SockServ m_sockServ; // Server socket. + Socket m_socket; }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 336ddc90..6d35a387 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -65,7 +65,7 @@ SockServ::~SockServ() { try { while(1) { ESP_LOGD(LOG_TAG, "Waiting on accept") - Socket tempSock = pSockServ->m_serverSocket.accept(pSockServ->getSSL()); + Socket tempSock = pSockServ->m_serverSocket.accept(); if (!tempSock.isValid()) { continue; } diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index 422d71cc..b7c41c1e 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -56,14 +56,12 @@ Socket::~Socket() { /** * @brief Accept a new socket. */ -Socket Socket::accept(bool useSSL) { +Socket Socket::accept() { struct sockaddr addr; getBind(&addr); - ESP_LOGD(LOG_TAG, ">> accept: Accepting on %s; sockFd: %d, using SSL: %d", addressToString(&addr).c_str(), m_sock, useSSL); + ESP_LOGD(LOG_TAG, ">> accept: Accepting on %s; sockFd: %d, using SSL: %d", addressToString(&addr).c_str(), m_sock, getSSL()); - struct sockaddr_in clientAddress; - socklen_t clientAddressLength = sizeof(clientAddress); - int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr *)&clientAddress, &clientAddressLength); + int clientSockFD = ::lwip_accept_r(m_sock, nullptr, nullptr); if (clientSockFD == -1) { SocketException se(errno); ESP_LOGE(LOG_TAG, "accept(): %s, m_sock=%d", strerror(errno), m_sock); @@ -73,7 +71,7 @@ Socket Socket::accept(bool useSSL) { ESP_LOGD(LOG_TAG, " - accept: Received new client!: sockFd: %d", clientSockFD); Socket newSocket; newSocket.m_sock = clientSockFD; - if (useSSL) { + if (getSSL()) { newSocket.setSSL(true); newSocket.m_sslSock.fd = clientSockFD; newSocket.sslHandshake(); @@ -252,7 +250,7 @@ bool Socket::isValid() { /** * @brief Create a listening socket. * @param [in] port The port number to listen upon. - * @param [in] isDatagram True if we are listening on a datagram. + * @param [in] isDatagram True if we are listening on a datagram. The default is false. */ void Socket::listen(uint16_t port, bool isDatagram) { ESP_LOGD(LOG_TAG, ">> listen: port: %d, isDatagram: %d", port, isDatagram); diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 33cce989..d644dfed 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -56,7 +56,7 @@ class Socket { Socket(); virtual ~Socket(); - Socket accept(bool useSSL=false); + Socket accept(); static std::string addressToString(struct sockaddr* addr); void bind(uint16_t port, uint32_t address); void close(); @@ -99,7 +99,7 @@ class SocketInputRecordStreambuf : public std::streambuf { ~SocketInputRecordStreambuf(); int_type underflow(); private: - char *m_buffer; + char* m_buffer; Socket m_socket; size_t m_dataLength; size_t m_bufferSize; From 313548882fda7643500343bb27a8324eaf907c4e Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 25 Nov 2017 17:34:20 -0600 Subject: [PATCH 083/310] Fix image link --- cpp_utils/ArduinoBLE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/ArduinoBLE.md b/cpp_utils/ArduinoBLE.md index d7a446af..fa5bda38 100644 --- a/cpp_utils/ArduinoBLE.md +++ b/cpp_utils/ArduinoBLE.md @@ -36,7 +36,7 @@ From October 2017 onwards, a build of the BLE libraries is supplied with the Ard ## Switching on debugging The BLE support contains extensive internal diagnostics which can be switched on through the `Tools > Core Debug Level` setting: -![](../../Documentation/images/arduino_debug.png) +![](https://github.com/nkolban/esp32-snippets/blob/master/Documentation/images/arduino_debug.png) ## Decoding an exception stack trace From a5e7ace62a338983d71d7965c84c8396d1e3e33d Mon Sep 17 00:00:00 2001 From: Jerzy Mikucki Date: Sun, 26 Nov 2017 02:06:30 +0100 Subject: [PATCH 084/310] Bug fix and cleanup. I2C version of HAL was not functional with current u8g2 version --- hardware/displays/U8G2/test_SSD1306.c | 8 +- hardware/displays/U8G2/test_SSD1306_i2c.c | 6 +- hardware/displays/U8G2/u8g2_esp32_hal.c | 151 ++++++---------------- hardware/displays/U8G2/u8g2_esp32_hal.h | 19 ++- 4 files changed, 59 insertions(+), 125 deletions(-) diff --git a/hardware/displays/U8G2/test_SSD1306.c b/hardware/displays/U8G2/test_SSD1306.c index 62f57cf1..16217017 100644 --- a/hardware/displays/U8G2/test_SSD1306.c +++ b/hardware/displays/U8G2/test_SSD1306.c @@ -40,16 +40,16 @@ void task_test_SSD1306(void *ignore) { u8g2_Setup_ssd1306_128x64_noname_f( &u8g2, U8G2_R0, - u8g2_esp32_msg_comms_cb, - u8g2_esp32_msg_gpio_and_delay_cb); // init u8g2 structure + u8g2_esp32_spi_byte_cb, + u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this, u8g2_SetPowerSave(&u8g2, 0); // wake up display u8g2_ClearBuffer(&u8g2); u8g2_DrawBox(&u8g2, 10,20, 20, 30); - u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); - u8g2_DrawStr(&u8g2, 0,15,"Hello World!"); + u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); + u8g2_DrawStr(&u8g2, 0,15,"Hello World!"); u8g2_SendBuffer(&u8g2); ESP_LOGD(tag, "All done!"); diff --git a/hardware/displays/U8G2/test_SSD1306_i2c.c b/hardware/displays/U8G2/test_SSD1306_i2c.c index a26d91fe..2ea8e6b4 100644 --- a/hardware/displays/U8G2/test_SSD1306_i2c.c +++ b/hardware/displays/U8G2/test_SSD1306_i2c.c @@ -26,12 +26,12 @@ void task_test_SSD1306i2c(void *ignore) { u8g2_t u8g2; // a structure which will contain all the data for one display - u8g2_Setup_ssd1306_128x32_univision_f( + u8g2_Setup_ssd1306_i2c_128x32_univision_f( &u8g2, U8G2_R0, //u8x8_byte_sw_i2c, - u8g2_esp32_msg_i2c_cb, - u8g2_esp32_msg_i2c_and_delay_cb); // init u8g2 structure + u8g2_esp32_i2c_byte_cb, + u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure u8x8_SetI2CAddress(&u8g2.u8x8,0x78); ESP_LOGI(TAG, "u8g2_InitDisplay"); diff --git a/hardware/displays/U8G2/u8g2_esp32_hal.c b/hardware/displays/U8G2/u8g2_esp32_hal.c index 49f71c4d..05a2c994 100644 --- a/hardware/displays/U8G2/u8g2_esp32_hal.c +++ b/hardware/displays/U8G2/u8g2_esp32_hal.c @@ -10,9 +10,11 @@ #include "u8g2_esp32_hal.h" static const char *TAG = "u8g2_hal"; +static const unsigned int I2C_TIMEOUT_MS = 1000; -static spi_device_handle_t handle; // SPI handle. -static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data. +static spi_device_handle_t handle_spi; // SPI handle. +static i2c_cmd_handle_t handle_i2c; // I2C handle. +static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data. #undef ESP_ERROR_CHECK #define ESP_ERROR_CHECK(x) do { esp_err_t rc = (x); if (rc != ESP_OK) { ESP_LOGE("err", "esp_err_t = %d", rc); assert(0 && #x);} } while(0); @@ -26,10 +28,10 @@ void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) { /* * HAL callback function as prescribed by the U8G2 library. This callback is invoked - * to handle callbacks for communications. + * to handle SPI communications. */ -uint8_t u8g2_esp32_msg_comms_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { - //ESP_LOGD(tag, "msg_comms_cb: Received a msg: %d: %s", msg, msgToString(msg, arg_int, arg_ptr)); +uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); switch(msg) { case U8X8_MSG_BYTE_SET_DC: if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { @@ -50,7 +52,7 @@ uint8_t u8g2_esp32_msg_comms_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void bus_config.miso_io_num = -1; // MISO bus_config.quadwp_io_num = -1; // Not used bus_config.quadhd_io_num = -1; // Not used - //ESP_LOGI(tag, "... Initializing bus."); + //ESP_LOGI(TAG, "... Initializing bus."); ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &bus_config, 1)); spi_device_interface_config_t dev_config; @@ -67,8 +69,8 @@ uint8_t u8g2_esp32_msg_comms_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void dev_config.queue_size = 200; dev_config.pre_cb = NULL; dev_config.post_cb = NULL; - //ESP_LOGI(tag, "... Adding device bus."); - ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle)); + //ESP_LOGI(TAG, "... Adding device bus."); + ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi)); break; } @@ -76,36 +78,35 @@ uint8_t u8g2_esp32_msg_comms_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void case U8X8_MSG_BYTE_SEND: { spi_transaction_t trans_desc; trans_desc.addr = 0; - trans_desc.command = 0; + trans_desc.cmd = 0; trans_desc.flags = 0; trans_desc.length = 8 * arg_int; // Number of bits NOT number of bytes. trans_desc.rxlength = 0; trans_desc.tx_buffer = arg_ptr; trans_desc.rx_buffer = NULL; - //ESP_LOGI(tag, "... Transmitting %d bytes.", arg_int); - ESP_ERROR_CHECK(spi_device_transmit(handle, &trans_desc)); + //ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int); + ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc)); break; } } return 0; -} // u8g2_esp32_msg_comms_cb +} // u8g2_esp32_spi_byte_cb /* * HAL callback function as prescribed by the U8G2 library. This callback is invoked - * to handle callbacks for communications. + * to handle I2C communications. */ -uint8_t u8g2_esp32_msg_i2c_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { - - ESP_LOGD(TAG, "msg_i2c_cb: Received a msg: %d %d", msg, arg_int); - //ESP_LOGD(tag, "msg_i2c_cb: Received a msg: %d: %s", msg, msgToString(msg, arg_int, arg_ptr)); +uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); switch(msg) { - case U8X8_MSG_BYTE_SET_DC: + case U8X8_MSG_BYTE_SET_DC: { if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { gpio_set_level(u8g2_esp32_hal.dc, arg_int); } break; + } case U8X8_MSG_BYTE_INIT: { if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED || @@ -127,108 +128,47 @@ uint8_t u8g2_esp32_msg_i2c_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void * ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf)); ESP_LOGI(TAG, "i2c_driver_install %d", I2C_MASTER_NUM); ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0)); -/* - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // dummy write - ESP_ERROR_CHECK(i2c_master_start(cmd)); - ESP_ERROR_CHECK(i2c_master_write_byte(cmd, 0x00 | I2C_MASTER_WRITE, ACK_CHECK_DIS)); - ESP_ERROR_CHECK(i2c_master_stop(cmd)); - ESP_LOGI(TAG, "i2c_master_cmd_begin %d", I2C_MASTER_NUM); - ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS)); - i2c_cmd_link_delete(cmd); -*/ - break; } case U8X8_MSG_BYTE_SEND: { - uint8_t *data; - uint8_t cmddata; - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - ESP_ERROR_CHECK(i2c_master_start(cmd)); -// ESP_LOGI(TAG, "I2CAddress %02X", u8x8_GetI2CAddress(u8x8)>>1); - ESP_ERROR_CHECK(i2c_master_write_byte(cmd, u8x8_GetI2CAddress(u8x8) | I2C_MASTER_WRITE, ACK_CHECK_EN)); - data = (uint8_t *)arg_ptr; - if (arg_int==1) { - cmddata=0; - ESP_ERROR_CHECK(i2c_master_write(cmd, &cmddata, 1, ACK_CHECK_EN)); -// printf("0x%02X ",zerodata); - } else { - cmddata=0x40; - ESP_ERROR_CHECK(i2c_master_write(cmd, &cmddata, 1, ACK_CHECK_EN)); - //bzero(arg_ptr,arg_int); - //*data=0x40; - } - //ESP_ERROR_CHECK(i2c_master_write(cmd, arg_ptr, arg_int, ACK_CHECK_EN)); + uint8_t* data_ptr = (uint8_t*)arg_ptr; + ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE); - while( arg_int > 0 ) { - ESP_ERROR_CHECK(i2c_master_write_byte(cmd, *data, ACK_CHECK_EN)); -// printf("0x%02X ",*data); - data++; + while( arg_int > 0 ) { + ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN)); + data_ptr++; arg_int--; - } -// printf("\n"); - - ESP_ERROR_CHECK(i2c_master_stop(cmd)); -// ESP_LOGI(TAG, "i2c_master_cmd_begin %d", I2C_MASTER_NUM); - ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS)); - i2c_cmd_link_delete(cmd); - break; - } - } - return 0; -} // u8g2_esp32_msg_i2c_cb - -/* - * HAL callback function as prescribed by the U8G2 library. This callback is invoked - * to handle callbacks for GPIO and delay functions. - */ -uint8_t u8g2_esp32_msg_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { - //ESP_LOGD(tag, "msg_gpio_and_delay_cb: Received a msg: %d: %s", msg, msgToString(msg, arg_int, arg_ptr)); - switch(msg) { - - // Initialize the GPIO and DELAY HAL functions. If the pins for DC and RESET have been - // specified then we define those pins as GPIO outputs. - case U8X8_MSG_GPIO_AND_DELAY_INIT: { - uint64_t bitmask = 0; - if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { - bitmask = bitmask | (1<>1); + ESP_ERROR_CHECK(i2c_master_start(handle_i2c)); + ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN)); break; + } - // Delay for the number of milliseconds passed in through arg_int. - case U8X8_MSG_DELAY_MILLI: - vTaskDelay(arg_int/portTICK_PERIOD_MS); + case U8X8_MSG_BYTE_END_TRANSFER: { + ESP_LOGD(TAG, "End I2C transfer."); + ESP_ERROR_CHECK(i2c_master_stop(handle_i2c)); + ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS)); + i2c_cmd_link_delete(handle_i2c); break; + } } return 0; -} // u8g2_esp32_msg_gpio_and_delay_cb +} // u8g2_esp32_i2c_byte_cb /* * HAL callback function as prescribed by the U8G2 library. This callback is invoked - * to handle callbacks for I²C and delay functions. + * to handle callbacks for GPIO and delay functions. */ -uint8_t u8g2_esp32_msg_i2c_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { - - ESP_LOGD(TAG, "msg_i2c_and_delay_cb: Received a msg: %d", msg); +uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); switch(msg) { // Initialize the GPIO and DELAY HAL functions. If the pins for DC and RESET have been @@ -244,12 +184,7 @@ uint8_t u8g2_esp32_msg_i2c_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_i if (u8g2_esp32_hal.cs != U8G2_ESP32_HAL_UNDEFINED) { bitmask = bitmask | (1< Date: Sun, 26 Nov 2017 16:35:40 -0600 Subject: [PATCH 085/310] Changes for #232 --- cpp_utils/Socket.h | 7 ++++++- cpp_utils/WS2812.cpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index d644dfed..6eef11db 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -7,7 +7,7 @@ #ifndef COMPONENTS_CPP_UTILS_SOCKET_H_ #define COMPONENTS_CPP_UTILS_SOCKET_H_ - +#include "sdkconfig.h" #include #include @@ -37,6 +37,11 @@ #include #include + +#if CONFIG_CXX_EXCEPTIONS != 1 +#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." +#endif + class SocketException: public std::exception { public: SocketException(int myErrno); diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index ca0a5b24..624480f0 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -10,6 +10,11 @@ #include "sdkconfig.h" #include "WS2812.h" +#if CONFIG_CXX_EXCEPTIONS != 1 +#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." +#endif + + static const char* LOG_TAG = "WS2812"; /** From a4eb6dd92139000bd236f37be6e14e4951288609 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 26 Nov 2017 20:55:49 -0600 Subject: [PATCH 086/310] Updates for #196 --- cpp_utils/BLEAdvertising.cpp | 222 ++++++++++++++++++++++++++++++-- cpp_utils/BLEAdvertising.h | 23 ++++ cpp_utils/BLEUUID.cpp | 26 ++++ cpp_utils/BLEUUID.h | 1 + cpp_utils/BLEUtils.cpp | 9 ++ cpp_utils/DesignNotes/BLECPP.md | 57 +++++++- 6 files changed, 325 insertions(+), 13 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index e5179a41..df4d220b 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -4,6 +4,17 @@ * This class encapsulates advertising a BLE Server. * Created on: Jun 21, 2017 * Author: kolban + * + * The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set + * of properties that are advertised and has built a data structure that can be populated by the programmer. + * This means that the programmer doesn't have to "mess with" the low level construction of a low level + * BLE advertising frame. Many of the fields are determined for us while others we can set before starting + * to advertise. + * + * Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters + * upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer + * set in the data will be advertised. + * */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) @@ -12,6 +23,7 @@ #include #include "BLEUtils.h" #include "GeneralUtils.h" + #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif @@ -44,6 +56,9 @@ BLEAdvertising::BLEAdvertising() { m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; m_advParams.channel_map = ADV_CHNL_ALL; m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + + m_customAdvData = false; // No custom advertising data + m_customScanResponseData = false; // No custom scan response data } // BLEAdvertising @@ -77,13 +92,47 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { } // setAppearance +/** + * @brief Set the advertisement data that is to be published in a regular advertisement. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setAdvertisementData"); + esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. + ESP_LOGD(LOG_TAG, "<< setAdvertisementData"); +} // setAdvertisementData + + +/** + * @brief Set the advertisement data that is to be published in a scan response. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setScanResponseData"); + esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. + ESP_LOGD(LOG_TAG, "<< setScanResponseData"); +} // setScanResponseData + /** * @brief Start advertising. * Start advertising. * @return N/A. */ void BLEAdvertising::start() { - ESP_LOGD(LOG_TAG, ">> start"); + ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) @@ -92,7 +141,7 @@ void BLEAdvertising::start() { int numServices = m_serviceUUIDs.size(); if (numServices > 0) { m_advData.service_uuid_len = 16*numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; uint8_t* p = m_advData.p_service_uuid; for (int i=0; i ESP_BLE_ADV_DATA_LEN_MAX) { + return; + } + m_payload.append(data); +} // addData + + + +/** + * @brief Set the complete services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { + char cdata[2]; + switch(uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + break; + } + + default: + return; + } +} // setCompleteServices + + +/** + * @brief Set the advertisement flags. + * @param [in] The flags to be set in the advertisement. + * + * * ESP_BLE_ADV_FLAG_LIMIT_DISC + * * ESP_BLE_ADV_FLAG_GEN_DISC + * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT + * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT + * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT + * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC + */ +void BLEAdvertisementData::setFlags(uint8_t flag) { + char cdata[3]; + cdata[0] = 2; + cdata[1] = ESP_BLE_AD_TYPE_FLAG; + cdata[2] = flag; + addData(std::string(cdata, 3)); +} // setFlag + + +/** + * @brief Set the name. + * @param [in] The complete name of the device. + */ +void BLEAdvertisementData::setName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setName"); +} // setName + + +/** + * @brief Set the partial services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { + char cdata[2]; + switch(uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + break; + } + + default: + return; + } +} // setPartialServices + + +/** + * @brief Set the short name. + * @param [in] The short name of the device. + */ +void BLEAdvertisementData::setShortName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setShortName"); +} // setShortName + + +/** + * @brief Retrieve the payload that is to be advertised. + * @return The payload that is to be advertised. + */ +std::string BLEAdvertisementData::getPayload() { + return m_payload; +} // getPayload + + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 6f315b8c..40a87f92 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -13,6 +13,25 @@ #include "BLEUUID.h" #include + +/** + * @brief Advertisement data set by the programmer to be published by the %BLE server. + */ +class BLEAdvertisementData { +public: + void setCompleteServices(BLEUUID uuid); + void setFlags(uint8_t); + void setName(std::string name); + void setPartialServices(BLEUUID uuid); + void setShortName(std::string name); +private: + friend class BLEAdvertising; + std::string m_payload; // The payload of the advertisement. + void addData(std::string data); // Add data to the payload. + std::string getPayload(); +}; // BLEAdvertisementData + + /** * @brief Perform and manage %BLE advertising. * @@ -26,10 +45,14 @@ class BLEAdvertising { void start(); void stop(); void setAppearance(uint16_t appearance); + void setAdvertisementData(BLEAdvertisementData& advertisementData); + void setScanResponseData(BLEAdvertisementData& advertisementData); private: esp_ble_adv_data_t m_advData; esp_ble_adv_params_t m_advParams; std::vector m_serviceUUIDs; + bool m_customAdvData; // Are we using custom advertising data? + bool m_customScanResponseData; // Are we using custom scan response data? }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index f5c6ceee..4fb48c37 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -188,6 +188,32 @@ BLEUUID::BLEUUID() { } // BLEUUID +/** + * @brief Get the number of bits in this uuid. + * @return The number of bits in the UUID. One of 16, 32 or 128. + */ +int BLEUUID::bitSize() { + if (m_valueSet == false) { + return 0; + } + switch(m_uuid.len) { + case ESP_UUID_LEN_16: { + return 16; + } + case ESP_UUID_LEN_32: { + return 32; + } + case ESP_UUID_LEN_128: { + return 128; + } + default: { + ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); + return 0; + } + } // End of switch +} // bitSize + + /** * @brief Compare a UUID against this UUID. * diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index bbe9b872..3089bcb1 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -24,6 +24,7 @@ class BLEUUID { BLEUUID(uint8_t* pData, size_t size, bool msbFirst); BLEUUID(esp_gatt_id_t gattId); BLEUUID(); + int bitSize(); // Get the number of bits in this uuid. bool equals(BLEUUID uuid); esp_bt_uuid_t* getNative(); BLEUUID to128(); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 5d1dec99..deaf7d2f 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1263,6 +1263,15 @@ void BLEUtils::dumpGapEvent( } // 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: { + ESP_LOGD(LOG_TAG, "[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 // diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md index db920dd3..1658f9fa 100644 --- a/cpp_utils/DesignNotes/BLECPP.md +++ b/cpp_utils/DesignNotes/BLECPP.md @@ -3,4 +3,59 @@ # BLE Server ## BLE Characteristic callbacks When a client connects to us as a BLE Server, it can change a characteristics value or read from a characteristic. The BLECharacteristicCallbacks class provides a handler for such. -# BLE Client \ No newline at end of file +# BLE Client +# Advertising +When we are being a BLE server we have the opportunity to perform advertising. Advertising means that we broadcast information about ourselves such that interested parties (presumably clients) can receive that information. The BLE advertising is described in the BLE specifications. + +Some key facts: + +* Advertisements can be sent periodically with the period being from 20ms to 10.24 seconds in steps of 0.625 ms. +* An advertisement payload can be up to 31 bytes in length. +* A payload can contain a sequence of records where a record is composed of a length, type and data. + +We can build a BLE advertising structure and specify that it should be sent for an advertisement using `esp_ble_gap_config_adv_data_raw` or sent using a scan response message using `esp_ble_gap_config_scan_rsp_data_raw`. + +Looking at the data structure for advertising we find it consists of records where each record is: + +* 1 byte length +* 1 byte record type +* length-1 bytes of data + +Trailing data (assuming we don't use all 31 bytes) shall be zeros. The advert types that include payload data are: + +* ADV_IND +* ADV\_NONCONN\_IND +* ADV\_SCAN\_IND + +The following advertising types are supported: + +| Type | Description | +|--------|------ +|0x01| Flags | +|0x02|Incomplete list of 16 bit service UUIDs +|0x03|Complete list of 16 bit service UUIDs +|0x04|Incomplete list of 32 bit service UUIDs +|0x05|Complete list of 32 bit UUIDs +|0x06|Incomplete list of 128 bit service UUIDs +|0x07|Complete list of 128 bit service UUIDs +|0x08|Shortened local name +|0x09|Complete local name + + + +See also: + +* BLE Spec 4.2 Vol 3 Part C Chapter 11 - Advertising and Scan response data format. +* [Advertisement record types](https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile) +* [A BLE Advertising Primer](http://www.argenox.com/a-ble-advertising-primer/) + +## Exposed functions +|Type|Function +|-|- +|0x01|`setFlags(uint8_t flag)` +|0x02, 0x04, 0x06|`setPartialServices(vector)` +|0x02, 0x04, 0x06|`setPartialServices(BLEUUID`) +|0x03, 0x05, 0x07|`setCompleteServices(vector)` +|0x03, 0x05, 0x07|`setCompleteServices(BLEUUID`) +|0x08|`setShortName(std::string)` +|0x09|`setName(std::string)` \ No newline at end of file From 32e0ee33db4543ae321ad3db8c5742c62e7c11e1 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 26 Nov 2017 22:40:57 -0600 Subject: [PATCH 087/310] More code for #196 --- cpp_utils/BLEAdvertising.cpp | 30 ++++++++++++++++++++++++++++++ cpp_utils/BLEAdvertising.h | 2 ++ cpp_utils/DesignNotes/BLECPP.md | 10 +++++++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index df4d220b..7406819b 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -219,6 +219,20 @@ void BLEAdvertisementData::addData(std::string data) { } // addData +/** + * @brief Set the appearance. + * @param [in] appearance The appearance code value. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ +void BLEAdvertisementData::setAppearance(uint16_t appearance) { + char cdata[2]; + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; + addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); +} // setAppearance + /** * @brief Set the complete services. @@ -277,6 +291,21 @@ void BLEAdvertisementData::setFlags(uint8_t flag) { } // setFlag + +/** + * @brief Set manufacturer specific data. + * @param [in] data Manufacturer data. + */ +void BLEAdvertisementData::setManufacturerData(std::string data) { + ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); + char cdata[2]; + cdata[0] = data.length() + 1; + cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; + addData(std::string(cdata, 2) + data); + ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); +} // setManufacturerData + + /** * @brief Set the name. * @param [in] The complete name of the device. @@ -342,6 +371,7 @@ void BLEAdvertisementData::setShortName(std::string name) { } // setShortName + /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 40a87f92..5c452a1a 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -19,8 +19,10 @@ */ class BLEAdvertisementData { public: + void setAppearance(uint16_t appearance); void setCompleteServices(BLEUUID uuid); void setFlags(uint8_t); + void setManufacturerData(std::string data); void setName(std::string name); void setPartialServices(BLEUUID uuid); void setShortName(std::string name); diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md index 1658f9fa..9ce9715a 100644 --- a/cpp_utils/DesignNotes/BLECPP.md +++ b/cpp_utils/DesignNotes/BLECPP.md @@ -40,6 +40,8 @@ The following advertising types are supported: |0x07|Complete list of 128 bit service UUIDs |0x08|Shortened local name |0x09|Complete local name +|0x19|Appearance +|0xFF|Manufacturer data @@ -54,8 +56,10 @@ See also: |-|- |0x01|`setFlags(uint8_t flag)` |0x02, 0x04, 0x06|`setPartialServices(vector)` -|0x02, 0x04, 0x06|`setPartialServices(BLEUUID`) +|0x02, 0x04, 0x06|`setPartialServices(BLEUUID)` |0x03, 0x05, 0x07|`setCompleteServices(vector)` -|0x03, 0x05, 0x07|`setCompleteServices(BLEUUID`) +|0x03, 0x05, 0x07|`setCompleteServices(BLEUUID)` |0x08|`setShortName(std::string)` -|0x09|`setName(std::string)` \ No newline at end of file +|0x09|`setName(std::string)` +|0x19|`setAppearance(uint16_t)` +|0xFF|`setManufacturerData(std::string)` \ No newline at end of file From bf9d53e16c32a2ca54b7171e547ac56d6652b000 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 27 Nov 2017 17:02:38 -0600 Subject: [PATCH 088/310] Changes for #233 --- networking/mqtt/paho_mqtt_embedded_c/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/networking/mqtt/paho_mqtt_embedded_c/README.md b/networking/mqtt/paho_mqtt_embedded_c/README.md index c9b4906a..830981ea 100644 --- a/networking/mqtt/paho_mqtt_embedded_c/README.md +++ b/networking/mqtt/paho_mqtt_embedded_c/README.md @@ -35,3 +35,6 @@ Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt). There is much more information available via the [MQTT community site](http://mqtt.org). + +# ESP32 Specific +The compilation requires that we enable mbedTLS debugging features. Run "make menuconfig" and visit `Component Config -> mbedTLS` and check the `Enable mbedTLS debugging` section. Save and rebuild the environment. From 56790e1b1b05fb0f4c1944ff49e63a11e55d08b8 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 27 Nov 2017 22:40:58 -0600 Subject: [PATCH 089/310] Updates for #228 --- cpp_utils/BLEClient.cpp | 24 +++++++- cpp_utils/BLEClient.h | 3 + cpp_utils/BLEExceptions.cpp | 9 +++ cpp_utils/BLEExceptions.h | 25 ++++++++ cpp_utils/BLERemoteCharacteristic.cpp | 13 ++++ cpp_utils/BLERemoteDescriptor.cpp | 25 +++++++- cpp_utils/BLERemoteDescriptor.h | 2 +- cpp_utils/BLERemoteService.cpp | 86 ++++++--------------------- cpp_utils/BLERemoteService.h | 4 +- cpp_utils/BLEService.h | 3 +- cpp_utils/BLEUtils.cpp | 53 ++++++++++++----- cpp_utils/Makefile.arduino | 2 + 12 files changed, 160 insertions(+), 89 deletions(-) create mode 100644 cpp_utils/BLEExceptions.cpp create mode 100644 cpp_utils/BLEExceptions.h diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 83e778dc..57999006 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -66,6 +66,21 @@ BLEClient::~BLEClient() { } // ~BLEClient +/** + * @brief Clear any existing services. + * + */ +void BLEClient::clearServices() { + ESP_LOGD(LOG_TAG, ">> clearServices"); + // Delete all the services. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); + ESP_LOGD(LOG_TAG, "<< clearServices"); +} // clearServices + + /** * @brief Connect to the partner (BLE Server). * @param [in] address The address of the partner. @@ -78,6 +93,8 @@ bool BLEClient::connect(BLEAddress address) { // and then block on its completion. When the event has arrived, we will have the handle. m_semaphoreRegEvt.take("connect"); + clearServices(); // Delete any services that may exist. + esp_err_t errRc = ::esp_ble_gattc_app_register(0); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -144,6 +161,9 @@ void BLEClient::gattClientEventHandler( case ESP_GATTC_DISCONNECT_EVT: { // If we receive a disconnect event, set the class flag that indicates that we are // no longer connected. + if (m_pClientCallbacks != nullptr) { + m_pClientCallbacks->onDisconnect(this); + } m_isConnected = false; break; } // ESP_GATTC_DISCONNECT_EVT @@ -332,7 +352,9 @@ std::map* BLEClient::getServices() { * and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received. */ ESP_LOGD(LOG_TAG, ">> getServices"); - m_servicesMap.clear(); + + clearServices(); // Clear any services that may exist. + esp_err_t errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index b24c71ae..9ff5f90b 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -15,6 +15,7 @@ #include #include #include +#include "BLEExceptions.h" #include "BLERemoteService.h" #include "BLEService.h" #include "BLEAddress.h" @@ -68,6 +69,7 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; + void clearServices(); // Clear any existing services. bool m_haveServices; // Have we previously obtain the set of services. }; // class BLEDevice @@ -79,6 +81,7 @@ class BLEClientCallbacks { public: virtual ~BLEClientCallbacks() {}; virtual void onConnect(BLEClient *pClient) = 0; + virtual void onDisconnect(BLEClient *pClient) = 0; }; #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEExceptions.cpp b/cpp_utils/BLEExceptions.cpp new file mode 100644 index 00000000..b6adfd82 --- /dev/null +++ b/cpp_utils/BLEExceptions.cpp @@ -0,0 +1,9 @@ +/* + * BLExceptions.cpp + * + * Created on: Nov 27, 2017 + * Author: kolban + */ + +#include "BLEExceptions.h" + diff --git a/cpp_utils/BLEExceptions.h b/cpp_utils/BLEExceptions.h new file mode 100644 index 00000000..d03ec6eb --- /dev/null +++ b/cpp_utils/BLEExceptions.h @@ -0,0 +1,25 @@ +/* + * BLExceptions.h + * + * Created on: Nov 27, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ +#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ +#include "sdkconfig.h" + +#if CONFIG_CXX_EXCEPTIONS != 1 +#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." +#endif + +#include + + +class BLEDisconnectedException : public std::exception { + const char *what() const throw () { + return "BLE Disconnected"; + } +}; + +#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 64b0a92d..d9c64c91 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -15,6 +15,7 @@ #include #include +#include "BLEExceptions.h" #include "BLEUtils.h" #include "GeneralUtils.h" #include "BLERemoteDescriptor.h" @@ -432,6 +433,12 @@ uint8_t BLERemoteCharacteristic::readUInt8(void) { std::string BLERemoteCharacteristic::readValue() { ESP_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle()); + // Check to see that we are connected. + if (!getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + m_semaphoreReadCharEvt.take("readValue"); // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. @@ -543,6 +550,12 @@ std::string BLERemoteCharacteristic::toString() { void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", newValue.length()); + // Check to see that we are connected. + if (!getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + m_semaphoreWriteCharEvt.take("writeValue"); // Invoke the ESP-IDF API to perform the write. diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 7a509f86..4d14ba80 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -27,6 +27,7 @@ BLERemoteDescriptor::BLERemoteDescriptor( m_pRemoteCharacteristic = pRemoteCharacteristic; } + /** * @brief Retrieve the handle associated with this remote descriptor. * @return The handle associated with this remote descriptor. @@ -36,6 +37,15 @@ uint16_t BLERemoteDescriptor::getHandle() { } // getHandle +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + /** * @brief Retrieve the UUID associated this remote descriptor. * @return The UUID associated this remote descriptor. @@ -46,6 +56,14 @@ BLEUUID BLERemoteDescriptor::getUUID() { std::string BLERemoteDescriptor::readValue(void) { + ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); + + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + m_semaphoreReadDescrEvt.take("readValue"); // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. @@ -66,7 +84,6 @@ std::string BLERemoteDescriptor::readValue(void) { ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); return m_value; - return ""; } // readValue @@ -119,6 +136,12 @@ void BLERemoteDescriptor::writeValue( size_t length, bool response) { ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + esp_err_t errRc = ::esp_ble_gattc_write_char_descr( m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), diff --git a/cpp_utils/BLERemoteDescriptor.h b/cpp_utils/BLERemoteDescriptor.h index c2cf3836..7bbc48f1 100644 --- a/cpp_utils/BLERemoteDescriptor.h +++ b/cpp_utils/BLERemoteDescriptor.h @@ -24,13 +24,13 @@ class BLERemoteCharacteristic; class BLERemoteDescriptor { public: uint16_t getHandle(); + BLERemoteCharacteristic* getRemoteCharacteristic(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); uint16_t readUInt16(void); uint32_t readUInt32(void); std::string toString(void); - //void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)); void writeValue(uint8_t* data, size_t length, bool response = false); void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 5227964e..1ad12759 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -115,7 +115,7 @@ void BLERemoteService::gattClientEventHandler( // Send the event to each of the characteristics owned by this service. for (auto &myPair : m_characteristicMap) { - myPair.first->gattClientEventHandler(event, gattc_if, evtParam); + myPair.second->gattClientEventHandler(event, gattc_if, evtParam); } } // gattClientEventHandler @@ -147,8 +147,8 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { } std::string v = uuid.toString(); for (auto &myPair : m_characteristicMap) { - if (myPair.second == v) { - return myPair.first; + if (myPair.first == v) { + return myPair.second; } } return nullptr; @@ -165,60 +165,6 @@ void BLERemoteService::retrieveCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); removeCharacteristics(); // Forget any previous characteristics. - /* - m_semaphoreGetCharEvt.take("getCharacteristics"); - - esp_err_t errRc = ::esp_ble_gattc_get_characteristic( - m_pClient->getGattcIf(), - m_pClient->getConnId(), - &m_srvcId, - nullptr); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreGetCharEvt.wait("getCharacteristics"); // Wait for the characteristics to become available. - - m_haveCharacteristics = true; // Remember that we have received the characteristics. - */ - //ESP_LOGE(LOG_TAG, "!!! NOT IMPLEMENTED !!!"); - //ESP_LOGD(LOG_TAG, "--- test code ---"); - /* - uint16_t count; - esp_gatt_status_t status = ::esp_ble_gattc_get_attr_count( - getClient()->getGattcIf(), - getClient()->getConnId(), - ESP_GATT_DB_CHARACTERISTIC, - m_startHandle, - m_endHandle, - 0, // Characteristic handle ... only used for ESP_GATT_DB_DESCRIPTOR - &count - ); - if (status != ESP_GATT_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_attr_count: %s", BLEUtils::gattStatusToString(status).c_str()); - } else { - ESP_LOGD(LOG_TAG, "Number of characteristics associated with service is %d", count); - } - - count = 1; - esp_gattc_service_elem_t srvcElem; - status = ::esp_ble_gattc_get_service( - getClient()->getGattcIf(), - getClient()->getConnId(), - &m_srvcId.uuid, // UUID of service - &srvcElem, // Records - &count, // records retrieved - 0 // offset - ); - if (status != ESP_GATT_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_service: %s", BLEUtils::gattStatusToString(status).c_str()); - } - else { - ESP_LOGD(LOG_TAG, "%s", BLEUtils::gattcServiceElementToString(&srvcElem).c_str()); - } - */ uint16_t offset = 0; esp_gattc_char_elem_t result; @@ -257,7 +203,7 @@ void BLERemoteService::retrieveCharacteristics() { this ); - m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic, pNewRemoteCharacteristic->getUUID().toString())); + m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); offset++; // Increment our count of number of descriptors found. } // Loop forever (until we break inside the loop). @@ -271,7 +217,7 @@ void BLERemoteService::retrieveCharacteristics() { * @brief Retrieve a map of all the characteristics of this service. * @return A map of all the characteristics of this service. */ -std::map* BLERemoteService::getCharacteristics() { +std::map * BLERemoteService::getCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); // If is possible that we have not read the characteristics associated with the service so do that // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking @@ -284,31 +230,35 @@ std::map* BLERemoteService::getCharacteri } // getCharacteristics +/** + * @brief Get the client associated with this service. + * @return A reference to the client associated with this service. + */ BLEClient* BLERemoteService::getClient() { return m_pClient; -} +} // getClient uint16_t BLERemoteService::getEndHandle() { return m_endHandle; -} +} // getEndHandle esp_gatt_id_t* BLERemoteService::getSrvcId() { return &m_srvcId; -} +} // getSrvcId uint16_t BLERemoteService::getStartHandle() { return m_startHandle; -} +} // getStartHandle + uint16_t BLERemoteService::getHandle() { ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str()); - //ESP_LOGE(LOG_TAG, "!!! getHandle: NOT IMPLEMENTED !!!"); ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle()); return getStartHandle(); -} +} // getHandle BLEUUID BLERemoteService::getUUID() { @@ -325,8 +275,8 @@ BLEUUID BLERemoteService::getUUID() { */ void BLERemoteService::removeCharacteristics() { for (auto &myPair : m_characteristicMap) { - delete myPair.first; - m_characteristicMap.erase(myPair.first); + delete myPair.second; + //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear } m_characteristicMap.clear(); // Clear the map } // removeCharacteristics @@ -343,7 +293,7 @@ std::string BLERemoteService::toString() { ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle << ", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle; for (auto &myPair : m_characteristicMap) { - ss << "\n" << myPair.first->toString(); + ss << "\n" << myPair.second->toString(); // myPair.second is the value } return ss.str(); diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 521effce..7f8c2f4a 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -33,7 +33,7 @@ class BLERemoteService { BLERemoteCharacteristic* getCharacteristic(const char* uuid); BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); - std::map* getCharacteristics(); + std::map* getCharacteristics(); void getCharacteristics(std::map* ptr); BLEClient* getClient(void); @@ -63,7 +63,7 @@ class BLERemoteService { // Properties // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. - std::map m_characteristicMap; + std::map m_characteristicMap; // We maintain a map of characteristics owned by this service keyed by a handle. std::map m_characteristicMapByHandle; diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index b37092f2..fdba54fd 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -80,8 +80,7 @@ class BLEService { BLECharacteristic* m_lastCreatedCharacteristic; BLEServer* m_pServer; BLEUUID m_uuid; - char deleteMe[10]; - //FreeRTOS::Semaphore m_serializeMutex; + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index deaf7d2f..d8d318f2 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -838,28 +838,52 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { } // 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) { - case ESP_GATT_CONN_UNKNOWN: + case ESP_GATT_CONN_UNKNOWN: { return "ESP_GATT_CONN_UNKNOWN"; - case ESP_GATT_CONN_L2C_FAILURE: + } + + case ESP_GATT_CONN_L2C_FAILURE: { return "ESP_GATT_CONN_L2C_FAILURE"; - case ESP_GATT_CONN_TIMEOUT: + } + + case ESP_GATT_CONN_TIMEOUT: { return "ESP_GATT_CONN_TIMEOUT"; - case ESP_GATT_CONN_TERMINATE_PEER_USER: + } + + case ESP_GATT_CONN_TERMINATE_PEER_USER: { return "ESP_GATT_CONN_TERMINATE_PEER_USER"; - case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: + } + + case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: { return "ESP_GATT_CONN_TERMINATE_LOCAL_HOST"; - case ESP_GATT_CONN_FAIL_ESTABLISH: + } + + case ESP_GATT_CONN_FAIL_ESTABLISH: { return "ESP_GATT_CONN_FAIL_ESTABLISH"; - case ESP_GATT_CONN_LMP_TIMEOUT: + } + + case ESP_GATT_CONN_LMP_TIMEOUT: { return "ESP_GATT_CONN_LMP_TIMEOUT"; - case ESP_GATT_CONN_CONN_CANCEL: + } + + case ESP_GATT_CONN_CONN_CANCEL: { return "ESP_GATT_CONN_CONN_CANCEL"; - case ESP_GATT_CONN_NONE: + } + + case ESP_GATT_CONN_NONE: { return "ESP_GATT_CONN_NONE"; - default: + } + + default: { return "Unknown"; + } } } // gattCloseReasonToString @@ -1388,11 +1412,12 @@ void BLEUtils::dumpGattClientEvent( // ESP_GATTC_DISCONNECT_EVT // // disconnect: - // - esp_gatt_status_t status - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGD(LOG_TAG, "[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() ); diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index df7fe54f..f9239250 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -26,6 +26,8 @@ BLE_FILES= \ BLEDescriptorMap.cpp \ BLEDevice.cpp \ BLEDevice.h \ + BLEExceptions.cpp \ + BLEExceptions.h \ BLERemoteCharacteristic.cpp \ BLERemoteCharacteristic.h \ BLERemoteDescriptor.cpp \ From ea59ac2d67ce83d6db9f5946b5528001c1e2cb6d Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 29 Nov 2017 23:49:37 -0600 Subject: [PATCH 090/310] Updates for #238 --- cpp_utils/BLEDevice.cpp | 13 +++++++++++++ cpp_utils/BLEDevice.h | 1 + 2 files changed, 14 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index a701459b..77957c60 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -12,6 +12,7 @@ #include #include #include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE @@ -157,6 +158,18 @@ void BLEDevice::gapEventHandler( } // gapEventHandler +/** + * @brief Get the BLE device address. + * @return The BLE device address. + */ +/* STATIC*/ BLEAddress BLEDevice::getAddress() { + const uint8_t* bdAddr = esp_bt_dev_get_address(); + esp_bd_addr_t addr; + memcpy(addr, bdAddr, sizeof(addr)); + return BLEAddress(addr); +} // getAddress + + /** * @brief Retrieve the Scan object that we use for scanning. * @return The scanning object reference. This is a singleton object. The caller should not diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index acf20f35..de0dfeea 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -29,6 +29,7 @@ class BLEDevice { static BLEClient* createClient(); static BLEServer* createServer(); static void dumpDevices(); + static BLEAddress getAddress(); static BLEScan* getScan(); static void init(std::string deviceName); From e6c816ebcb018745664d90c680edb131b323c2c1 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 2 Dec 2017 10:20:49 -0600 Subject: [PATCH 091/310] Changes for #241 --- cpp_utils/OV7670.cpp | 2 +- cpp_utils/PWM.cpp | 2 +- cpp_utils/PWM.h | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp_utils/OV7670.cpp b/cpp_utils/OV7670.cpp index 9e4fa7a1..c2de1316 100644 --- a/cpp_utils/OV7670.cpp +++ b/cpp_utils/OV7670.cpp @@ -189,7 +189,7 @@ static esp_err_t camera_enable_out_clock(camera_config_t* config) periph_module_enable(PERIPH_LEDC_MODULE); ledc_timer_config_t timer_conf; - timer_conf.bit_num = (ledc_timer_bit_t)1; + timer_conf.duty_resolution = (ledc_timer_bit_t)1; timer_conf.freq_hz = config->xclk_freq_hz; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; timer_conf.timer_num = config->ledc_timer; diff --git a/cpp_utils/PWM.cpp b/cpp_utils/PWM.cpp index 3155b42f..85e0b23f 100644 --- a/cpp_utils/PWM.cpp +++ b/cpp_utils/PWM.cpp @@ -33,7 +33,7 @@ */ PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t bitSize, ledc_timer_t timer, ledc_channel_t channel) { ledc_timer_config_t timer_conf; - timer_conf.bit_num = bitSize; + timer_conf.duty_resolution = bitSize; timer_conf.freq_hz = frequency; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; timer_conf.timer_num = timer; diff --git a/cpp_utils/PWM.h b/cpp_utils/PWM.h index fdc1fcf1..b8bfb317 100644 --- a/cpp_utils/PWM.h +++ b/cpp_utils/PWM.h @@ -22,10 +22,10 @@ class PWM { public: PWM( int gpioNum, - uint32_t frequency = 100, + uint32_t frequency = 100, ledc_timer_bit_t bitSize = LEDC_TIMER_10_BIT, - ledc_timer_t timer = LEDC_TIMER_0, - ledc_channel_t channel = LEDC_CHANNEL_0); + ledc_timer_t timer = LEDC_TIMER_0, + ledc_channel_t channel = LEDC_CHANNEL_0); uint32_t getDuty(); uint32_t getFrequency(); From 33c07ab5baccb876468235fd782152c6bef336d4 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 2 Dec 2017 15:16:14 -0600 Subject: [PATCH 092/310] Changes for #242 --- cpp_utils/BLEAdvertising.h | 10 ++- cpp_utils/BLECharacteristic.cpp | 1 - cpp_utils/BLEDevice.cpp | 34 ++++++-- cpp_utils/BLEDevice.h | 16 ++-- cpp_utils/BLEExceptions.h | 6 ++ cpp_utils/BLERemoteService.cpp | 30 ++++++- cpp_utils/BLERemoteService.h | 2 + cpp_utils/BLEServer.cpp | 145 ++++++++++++++------------------ cpp_utils/BLEServer.h | 2 +- cpp_utils/BLEUUID.cpp | 48 +++++++---- cpp_utils/BLEUUID.h | 2 +- cpp_utils/PWM.cpp | 4 +- 12 files changed, 174 insertions(+), 126 deletions(-) diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 5c452a1a..433de603 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -18,6 +18,9 @@ * @brief Advertisement data set by the programmer to be published by the %BLE server. */ class BLEAdvertisementData { + // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will + // be exposed on demand/request or as time permits. + // public: void setAppearance(uint16_t appearance); void setCompleteServices(BLEUUID uuid); @@ -26,11 +29,13 @@ class BLEAdvertisementData { void setName(std::string name); void setPartialServices(BLEUUID uuid); void setShortName(std::string name); + private: friend class BLEAdvertising; std::string m_payload; // The payload of the advertisement. - void addData(std::string data); // Add data to the payload. - std::string getPayload(); + + void addData(std::string data); // Add data to the payload. + std::string getPayload(); // Retrieve the current advert payload. }; // BLEAdvertisementData @@ -49,6 +54,7 @@ class BLEAdvertising { void setAppearance(uint16_t appearance); void setAdvertisementData(BLEAdvertisementData& advertisementData); void setScanResponseData(BLEAdvertisementData& advertisementData); + private: esp_ble_adv_data_t m_advData; esp_ble_adv_params_t m_advParams; diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 6d746cf2..4c0935a7 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -321,7 +321,6 @@ void BLECharacteristic::handleGATTServerEvent( // - bool need_rsp // case ESP_GATTS_READ_EVT: { - ESP_LOGD(LOG_TAG, "- Testing: 0x%.2x == 0x%.2x", param->read.handle, m_handle); if (param->read.handle == m_handle) { if (m_pCallbacks != nullptr) { diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 77957c60..e1ace955 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -40,12 +40,14 @@ static const char* LOG_TAG = "BLEDevice"; BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; -bool initialized = false; +bool initialized = false; // Have we been initialized? + + /** * @brief Create a new instance of a client. * @return A new instance of the client. */ -BLEClient* BLEDevice::createClient() { +/* STATIC */ BLEClient* BLEDevice::createClient() { m_pClient = new BLEClient(); return m_pClient; } // createClient @@ -55,7 +57,7 @@ BLEClient* BLEDevice::createClient() { * @brief Create a new instance of a server. * @return A new instance of the server. */ -BLEServer* BLEDevice::createServer() { +/* STATIC */ BLEServer* BLEDevice::createServer() { ESP_LOGD(LOG_TAG, ">> createServer"); m_pServer = new BLEServer(); m_pServer->createApp(0); @@ -71,7 +73,7 @@ BLEServer* BLEDevice::createServer() { * @param [in] gatts_if The connection to the GATT interface. * @param [in] param Parameters for the event. */ -void BLEDevice::gattServerEventHandler( +/* STATIC */ void BLEDevice::gattServerEventHandler( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param @@ -97,7 +99,7 @@ void BLEDevice::gattServerEventHandler( * @param [in] gattc_if * @param [in] param */ -void BLEDevice::gattClientEventHandler( +/* STATIC */ void BLEDevice::gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param) { @@ -124,7 +126,7 @@ void BLEDevice::gattClientEventHandler( /** * @brief Handle GAP events. */ -void BLEDevice::gapEventHandler( +/* STATIC */ void BLEDevice::gapEventHandler( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { @@ -175,7 +177,7 @@ void BLEDevice::gapEventHandler( * @return The scanning object reference. This is a singleton object. The caller should not * try and release/delete it. */ -BLEScan* BLEDevice::getScan() { +/* STATIC */ BLEScan* BLEDevice::getScan() { //ESP_LOGD(LOG_TAG, ">> getScan"); if (m_pScan == nullptr) { m_pScan = new BLEScan(); @@ -190,9 +192,10 @@ BLEScan* BLEDevice::getScan() { * @brief Initialize the %BLE environment. * @param deviceName The device name of the device. */ -void BLEDevice::init(std::string deviceName) { +/* STATIC */ void BLEDevice::init(std::string deviceName) { if(!initialized){ - initialized = true; + initialized = true; // Set the initialization flag to ensure we are only initialized once. + esp_err_t errRc = ::nvs_flash_init(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -205,6 +208,7 @@ void BLEDevice::init(std::string deviceName) { ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } + #ifndef CLASSIC_BT_ENABLED // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); @@ -220,6 +224,7 @@ void BLEDevice::init(std::string deviceName) { return; } #endif + errRc = esp_bluedroid_init(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -289,4 +294,15 @@ void BLEDevice::init(std::string deviceName) { ESP_LOGD(LOG_TAG, "<< setPower"); } // setPower + +/** + * @brief Return a string representation of the nature of this device. + * @return A string representation of the nature of this device. + */ +/* STATIC */ std::string BLEDevice::toString() { + std::ostringstream oss; + oss << "BD Address: " << getAddress().toString(); + return oss.str(); +} // toString + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index de0dfeea..09a2098f 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -26,12 +26,13 @@ class BLEDevice { public: - static BLEClient* createClient(); - static BLEServer* createServer(); - static void dumpDevices(); - static BLEAddress getAddress(); - static BLEScan* getScan(); - static void init(std::string deviceName); + static BLEClient* createClient(); + static BLEServer* createServer(); + static BLEAddress getAddress(); + static BLEScan* getScan(); + static void init(std::string deviceName); + static void setPower(esp_power_level_t powerLevel); + static std::string toString(); private: static BLEServer *m_pServer; @@ -54,9 +55,6 @@ class BLEDevice { esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); -public: - static void setPower(esp_power_level_t powerLevel); - }; // class BLE #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEExceptions.h b/cpp_utils/BLEExceptions.h index d03ec6eb..369fcaf6 100644 --- a/cpp_utils/BLEExceptions.h +++ b/cpp_utils/BLEExceptions.h @@ -22,4 +22,10 @@ class BLEDisconnectedException : public std::exception { } }; +class BLEUuidNotFoundException : public std::exception { + const char *what() const throw () { + return "No such UUID"; + } +}; + #endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 1ad12759..78ff9914 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -124,6 +124,7 @@ void BLERemoteService::gattClientEventHandler( * @brief Get the remote characteristic object for the characteristic UUID. * @param [in] uuid Remote characteristic uuid. * @return Reference to the remote characteristic object. + * @throws BLEUuidNotFoundException */ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { return getCharacteristic(BLEUUID(uuid)); @@ -134,6 +135,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { * @brief Get the characteristic object for the UUID. * @param [in] uuid Characteristic uuid. * @return Reference to the characteristic object. + * @throws BLEUuidNotFoundException */ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { // Design @@ -151,7 +153,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { return myPair.second; } } - return nullptr; + throw new BLEUuidNotFoundException(); } // getCharacteristic @@ -265,6 +267,17 @@ BLEUUID BLERemoteService::getUUID() { return m_uuid; } +/** + * @brief Read the value of a characteristic associated with this service. + */ +std::string BLERemoteService::getValue(BLEUUID characteristicUuid) { + ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); + std::string ret = getCharacteristic(characteristicUuid)->readValue(); + ESP_LOGD(LOG_TAG, "<< readValue"); + return ret; +} // readValue + + /** * @brief Delete the characteristics in the characteristics map. @@ -282,6 +295,18 @@ void BLERemoteService::removeCharacteristics() { } // removeCharacteristics +/** + * @brief Set the value of a characteristic. + * @param [in] characteristicUuid The characteristic to set. + * @param [in] value The value to set. + * @throws BLEUuidNotFound + */ +void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); + getCharacteristic(characteristicUuid)->writeValue(value); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue + /** * @brief Create a string representation of this remote service. @@ -300,7 +325,4 @@ std::string BLERemoteService::toString() { } // toString - - - #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 7f8c2f4a..b60e4fb3 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -39,6 +39,8 @@ class BLERemoteService { BLEClient* getClient(void); uint16_t getHandle(); BLEUUID getUUID(void); + std::string getValue(BLEUUID characteristicUuid); + void setValue(BLEUUID characteristicUuid, std::string value); std::string toString(void); private: diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 0d9bd6b5..448c8031 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -178,108 +178,114 @@ void BLEServer::handleGATTServerEvent( m_serviceMap.handleGATTServerEvent(event, gatts_if, param); switch(event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + // + case ESP_GATTS_ADD_CHAR_EVT: { + break; + } // ESP_GATTS_ADD_CHAR_EVT // ESP_GATTS_CONNECT_EVT // connect: - // - uint16_t conn_id + // - uint16_t conn_id // - esp_bd_addr_t remote_bda - // - bool is_connected + // - bool is_connected + // case ESP_GATTS_CONNECT_EVT: { m_connId = param->connect.conn_id; // Save the connection id. if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); } - m_connectedCount++; + m_connectedCount++; // Increment the number of connected devices count. break; } // ESP_GATTS_CONNECT_EVT - // ESP_GATTS_REG_EVT - // reg: - // - esp_gatt_status_t status - // - uint16_t app_id - case ESP_GATTS_REG_EVT: { - m_gatts_if = gatts_if; - m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. - break; - } // ESP_GATTS_REG_EVT - - // ESP_GATTS_CREATE_EVT // Called when a new service is registered as having been created. // // create: - // * esp_gatt_status_t status - // * uint16_t service_handle + // * esp_gatt_status_t status + // * uint16_t service_handle // * esp_gatt_srvc_id_t service_id // case ESP_GATTS_CREATE_EVT: { BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid); m_serviceMap.setByHandle(param->create.service_handle, pService); - //pService->setHandle(param->create.service_handle); m_semaphoreCreateEvt.give(); break; } // ESP_GATTS_CREATE_EVT + // ESP_GATTS_DISCONNECT_EVT + // + // disconnect + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - bool is_connected + // + // If we receive a disconnect event then invoke the callback for disconnects (if one is present). + // we also want to start advertising again. + case ESP_GATTS_DISCONNECT_EVT: { + m_connectedCount--; // Decrement the number of connected devices count. + if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. + m_pServerCallbacks->onDisconnect(this); + } + startAdvertising(); + break; + } // ESP_GATTS_DISCONNECT_EVT + + // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. // // read: - // - uint16_t conn_id - // - uint32_t trans_id + // - uint16_t conn_id + // - uint32_t trans_id // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool is_long - // - bool need_rsp + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp // case ESP_GATTS_READ_EVT: { break; } // ESP_GATTS_READ_EVT + // ESP_GATTS_REG_EVT + // reg: + // - esp_gatt_status_t status + // - uint16_t app_id + // + case ESP_GATTS_REG_EVT: { + m_gatts_if = gatts_if; + m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. + break; + } // ESP_GATTS_REG_EVT + + // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. // // write: - // - uint16_t conn_id - // - uint16_t trans_id + // - uint16_t conn_id + // - uint16_t trans_id // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool need_rsp - // - bool is_prep - // - uint16_t len - // - uint8_t *value - + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t* value + // case ESP_GATTS_WRITE_EVT: { break; } - // ESP_GATTS_DISCONNECT_EVT - // If we receive a disconnect event then invoke the callback for disconnects (if one is present). - // we also want to start advertising again. - case ESP_GATTS_DISCONNECT_EVT: { - m_connectedCount--; - if (m_pServerCallbacks != nullptr) { - m_pServerCallbacks->onDisconnect(this); - } - startAdvertising(); - break; - } // ESP_GATTS_DISCONNECT_EVT - - - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - case ESP_GATTS_ADD_CHAR_EVT: { - break; - } // ESP_GATTS_ADD_CHAR_EVT - - default: { break; } @@ -303,7 +309,7 @@ void BLEServer::registerApp() { /** - * @brief Set the callbacks. + * @brief Set the server callbacks. * * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client * disconnecting. This function can be called to register a callback handler that will be invoked when these @@ -329,36 +335,15 @@ void BLEServer::startAdvertising() { } // startAdvertising -/* -void BLEServer::addCharacteristic(BLECharacteristic *characteristic, BLEService *pService) { - ESP_LOGD(tag, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s, serviceHandle=0x%.2x", - characteristic->m_bleUUID.toString().c_str(), - pService->getHandle()); - - m_characteristicMap.setByUUID(characteristic->m_bleUUID, characteristic); - - esp_err_t errRc = ::esp_ble_gatts_add_char( - pService->getHandle(), - characteristic->getUUID().getNative(), - (esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), - characteristic->getProperties(), - &characteristic->m_value, - NULL); - - if (errRc != ESP_OK) { - ESP_LOGE(tag, "esp_ble_gatts_add_char: rc=%d %s", errRc, espToString(errRc)); - return; - } -} -*/ - void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 64b34b41..524e88f3 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -55,7 +55,7 @@ class BLEServer { BLEService* createService(const char* uuid); BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); BLEAdvertising* getAdvertising(); - void setCallbacks(BLEServerCallbacks *pCallbacks); + void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 4fb48c37..9ca7cdd7 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "BLEUUID.h" static const char* LOG_TAG = "BLEUUID"; @@ -242,6 +244,35 @@ bool BLEUUID::equals(BLEUUID uuid) { } // equals +/** + * Create a BLEUUID from a string of the form: + * 0xNNNN + * 0xNNNNNNNN + * 0x + * NNNN + * NNNNNNNN + * + */ +BLEUUID BLEUUID::fromString(std::string _uuid){ + uint8_t start = 0; + if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. + start = 2; + } + uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. + + if( len == 4) { + uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + } else if (len == 8) { + uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + } else if (len == 36) { + return BLEUUID(_uuid); + } + return BLEUUID(); +} // fromString + + /** * @brief Get the native UUID value. * @@ -376,21 +407,4 @@ std::string BLEUUID::toString() { return ss.str(); } // toString -BLEUUID BLEUUID::fromString(std::string _uuid){ - uint8_t start = 0; - if(strstr(_uuid.c_str(), "0x") != nullptr){ - start = 2; - } - uint8_t len = _uuid.length() - start; - if(len == 4 ){ - uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); - return BLEUUID(x); - }else if(len == 8){ - uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); - return BLEUUID(x); - }else if (len == 36){ - return BLEUUID(_uuid); - } - return BLEUUID(); -} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index 3089bcb1..5fb7795b 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -29,7 +29,7 @@ class BLEUUID { esp_bt_uuid_t* getNative(); BLEUUID to128(); std::string toString(); - static BLEUUID fromString(std::string uuid); + static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string private: esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. diff --git a/cpp_utils/PWM.cpp b/cpp_utils/PWM.cpp index 85e0b23f..67659cb1 100644 --- a/cpp_utils/PWM.cpp +++ b/cpp_utils/PWM.cpp @@ -24,14 +24,14 @@ * * @param [in] gpioNum The GPIO pin to use for output. * @param [in] frequency The frequency of the period. - * @param [in] bitSize The size in bits of the timer. Allowed values are LEDC_TIMER_10_BIT, + * @param [in] dutyResolution The size in bits of the timer. Allowed values are LEDC_TIMER_10_BIT, * LEDC_TIMER_11_BIT, LEDC_TIMER_12_BIT, LEDC_TIMER_13_BIT, LEDC_TIMER_14_BIT, LEDC_TIMER_15_BIT. * @param [in] timer The timer to use. A value of LEDC_TIMER0, LEDC_TIMER1, LEDC_TIMER2 or LEDC_TIMER3. * @param [in] channel The channel to use. A value from LEDC_CHANNEL0 to LEDC_CHANNEL1. * @return N/A. */ -PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t bitSize, ledc_timer_t timer, ledc_channel_t channel) { +PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t dutyResolution, ledc_timer_t timer, ledc_channel_t channel) { ledc_timer_config_t timer_conf; timer_conf.duty_resolution = bitSize; timer_conf.freq_hz = frequency; From 0b91cfea0b904404251ed6def92f25d94227271b Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 2 Dec 2017 15:22:38 -0600 Subject: [PATCH 093/310] More changes for #241 --- cpp_utils/PWM.cpp | 22 +++++++++++----------- cpp_utils/PWM.h | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp_utils/PWM.cpp b/cpp_utils/PWM.cpp index 67659cb1..f7119c4a 100644 --- a/cpp_utils/PWM.cpp +++ b/cpp_utils/PWM.cpp @@ -33,7 +33,7 @@ */ PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t dutyResolution, ledc_timer_t timer, ledc_channel_t channel) { ledc_timer_config_t timer_conf; - timer_conf.duty_resolution = bitSize; + timer_conf.duty_resolution = dutyResolution; timer_conf.freq_hz = frequency; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; timer_conf.timer_num = timer; @@ -48,9 +48,9 @@ PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t dutyResolution, ledc_ ledc_conf.timer_sel = timer; ESP_ERROR_CHECK(::ledc_channel_config(&ledc_conf)); - this->channel = channel; - this->timer = timer; - this->bitSize = bitSize; + this->m_channel = channel; + this->m_timer = timer; + this->m_dutyResolution = dutyResolution; } // PWM @@ -60,7 +60,7 @@ PWM::PWM(int gpioNum, uint32_t frequency, ledc_timer_bit_t dutyResolution, ledc_ * @return The duty cycle value. */ uint32_t PWM::getDuty() { - return ::ledc_get_duty(LEDC_HIGH_SPEED_MODE, channel); + return ::ledc_get_duty(LEDC_HIGH_SPEED_MODE, m_channel); } // getDuty @@ -70,7 +70,7 @@ uint32_t PWM::getDuty() { * @return The frequency/period in Hz. */ uint32_t PWM::getFrequency() { - return ::ledc_get_freq(LEDC_HIGH_SPEED_MODE, timer); + return ::ledc_get_freq(LEDC_HIGH_SPEED_MODE, m_timer); } // getFrequency @@ -84,8 +84,8 @@ uint32_t PWM::getFrequency() { * @return N/A. */ void PWM::setDuty(uint32_t duty) { - ESP_ERROR_CHECK(::ledc_set_duty(LEDC_HIGH_SPEED_MODE, channel, duty)); - ESP_ERROR_CHECK(::ledc_update_duty(LEDC_HIGH_SPEED_MODE, channel)); + ESP_ERROR_CHECK(::ledc_set_duty(LEDC_HIGH_SPEED_MODE, m_channel, duty)); + ESP_ERROR_CHECK(::ledc_update_duty(LEDC_HIGH_SPEED_MODE, m_channel)); } // setDuty @@ -97,7 +97,7 @@ void PWM::setDuty(uint32_t duty) { */ void PWM::setDutyPercentage(uint8_t percent) { uint32_t max; - switch(bitSize) { + switch(m_dutyResolution) { case LEDC_TIMER_10_BIT: max = 1 << 10; break; @@ -139,7 +139,7 @@ void PWM::setDutyPercentage(uint8_t percent) { * @return N/A. */ void PWM::setFrequency(uint32_t freq) { - ESP_ERROR_CHECK(::ledc_set_freq(LEDC_HIGH_SPEED_MODE, timer, freq)); + ESP_ERROR_CHECK(::ledc_set_freq(LEDC_HIGH_SPEED_MODE, m_timer, freq)); } // setFrequency @@ -150,5 +150,5 @@ void PWM::setFrequency(uint32_t freq) { * @return N/A. */ void PWM::stop(bool idleLevel) { - ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, channel, idleLevel)); + ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, m_channel, idleLevel)); } // stop diff --git a/cpp_utils/PWM.h b/cpp_utils/PWM.h index b8bfb317..01c30a48 100644 --- a/cpp_utils/PWM.h +++ b/cpp_utils/PWM.h @@ -34,9 +34,9 @@ class PWM { void setFrequency(uint32_t freq); void stop(bool idleLevel=false); private: - ledc_channel_t channel; - ledc_timer_t timer; - ledc_timer_bit_t bitSize; // Bit size of timer. + ledc_channel_t m_channel; + ledc_timer_t m_timer; + ledc_timer_bit_t m_dutyResolution; // Bit size of timer. }; #endif /* COMPONENTS_CPP_UTILS_PWM_H_ */ From c4d6a3d87a96e792d03891199253fae4de3face3 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 3 Dec 2017 12:08:56 -0600 Subject: [PATCH 094/310] Changes for #242 --- cpp_utils/BLECharacteristic.h | 3 ++- cpp_utils/BLEClient.cpp | 36 +++++++++++++++++++++++++++++++++-- cpp_utils/BLEClient.h | 34 +++++++++++++++++++++------------ cpp_utils/BLEDevice.cpp | 32 +++++++++++++++++++++++++++++++ cpp_utils/BLEDevice.h | 17 ++++++++++------- cpp_utils/BLERemoteService.h | 33 +++++++++++++++++--------------- 6 files changed, 118 insertions(+), 37 deletions(-) diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index cacf1e50..09301834 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -63,10 +63,11 @@ class BLECharacteristic { BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); //size_t getLength(); BLEUUID getUUID(); - std::string getValue(); + void indicate(); void notify(); + std::string getValue(); void setBroadcastProperty(bool value); void setCallbacks(BLECharacteristicCallbacks* pCallbacks); void setIndicateProperty(bool value); diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 57999006..189d5d74 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -117,7 +117,7 @@ bool BLEClient::connect(BLEAddress address) { return false; } - uint32_t rc = m_semaphoreOpenEvt.wait("connect"); + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); return rc == ESP_GATT_OK; } // connect @@ -313,6 +313,7 @@ BLERemoteService* BLEClient::getService(const char* uuid) { * @brief Get the service object corresponding to the uuid. * @param [in] uuid The UUID of the service being sought. * @return A reference to the Service or nullptr if don't know about it. + * @throws BLEUuidNotFound */ BLERemoteService* BLEClient::getService(BLEUUID uuid) { ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); @@ -333,7 +334,7 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) { } } // End of each of the services. ESP_LOGD(LOG_TAG, "<< getService: not found"); - return nullptr; + throw new BLEUuidNotFoundException; } // getService @@ -371,6 +372,21 @@ std::map* BLEClient::getServices() { return &m_servicesMap; } // getServices + +/** + * @brief Get the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to read. + * @throws BLEUuidNotFound + */ +std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue(); + ESP_LOGD(LOG_TAG, "<> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue + + /** * @brief Return a string representation of this client. * @return A string representation of this client. @@ -433,4 +464,5 @@ std::string BLEClient::toString() { return ss.str(); } // toString + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 9ff5f90b..a60ed102 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -30,19 +30,28 @@ class BLEClient { public: BLEClient(); ~BLEClient(); - bool connect(BLEAddress address); - void disconnect(); - BLEAddress getPeerAddress(); - int getRssi(); - std::map* getServices(); - BLERemoteService* getService(const char* uuid); - BLERemoteService* getService(BLEUUID uuid); + + bool connect(BLEAddress address); // Connect to the remote BLE Server + void disconnect(); // Disconnect from the remote BLE Server + BLEAddress getPeerAddress(); // Get the address of the remote BLE Server + int getRssi(); // Get the RSSI of the remote BLE Server + std::map* getServices(); // Get a map of the services offered by the remote BLE Server + BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server. + BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server. + std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service. + + void handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); - bool isConnected(); + + bool isConnected(); // Return true if we are connected. + void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); - std::string toString(); + void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. + + std::string toString(); // Return a string representation of this client. + private: friend class BLEDevice; @@ -57,11 +66,12 @@ class BLEClient { uint16_t getConnId(); esp_gatt_if_t getGattcIf(); - BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); + BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. uint16_t m_conn_id; // int m_deviceType; esp_gatt_if_t m_gattc_if; - bool m_isConnected; + bool m_haveServices; // Have we previously obtain the set of services from the remote server. + bool m_isConnected; // Are we currently connected. BLEClientCallbacks* m_pClientCallbacks; FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); @@ -70,7 +80,7 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; void clearServices(); // Clear any existing services. - bool m_haveServices; // Have we previously obtain the set of services. + }; // class BLEDevice diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index e1ace955..26b4203e 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -188,6 +188,23 @@ bool initialized = false; // Have we been initialized? } // getScan +/** + * @brief Get the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + std::string ret = pClient->getValue(serviceUUID, characteristicUUID); + pClient->disconnect(); + ESP_LOGD(LOG_TAG, "<< getValue"); + return ret; +} // getValue + + /** * @brief Initialize the %BLE environment. * @param deviceName The device name of the device. @@ -295,6 +312,21 @@ bool initialized = false; // Have we been initialized? } // setPower +/** + * @brief Set the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + pClient->setValue(serviceUUID, characteristicUUID, value); + pClient->disconnect(); +} // setValue + + /** * @brief Return a string representation of the nature of this device. * @return A string representation of the nature of this device. diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 09a2098f..5399ff7c 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -20,19 +20,22 @@ #include "BLEUtils.h" #include "BLEScan.h" #include "BLEAddress.h" + /** * @brief %BLE functions. */ class BLEDevice { public: - static BLEClient* createClient(); - static BLEServer* createServer(); - static BLEAddress getAddress(); - static BLEScan* getScan(); - static void init(std::string deviceName); - static void setPower(esp_power_level_t powerLevel); - static std::string toString(); + static BLEClient* createClient(); // Create a new BLE client. + static BLEServer* createServer(); // Cretae a new BLE server. + static BLEAddress getAddress(); // Retrieve our own local BD address. + static BLEScan* getScan(); // Get the scan object + static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server. + static void init(std::string deviceName); // Initialize the local BLE environment. + static void setPower(esp_power_level_t powerLevel); // Set our power level. + static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server. + static std::string toString(); // Return a string representation of our device. private: static BLEServer *m_pServer; diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index b60e4fb3..222c6e45 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -30,17 +30,17 @@ class BLERemoteService { virtual ~BLERemoteService(); // Public methods - BLERemoteCharacteristic* getCharacteristic(const char* uuid); - BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); - BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); + BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. + BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. + BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. std::map* getCharacteristics(); - void getCharacteristics(std::map* ptr); + void getCharacteristics(std::map* pCharacteristicMap); // Get the characteristics map. - BLEClient* getClient(void); - uint16_t getHandle(); - BLEUUID getUUID(void); - std::string getValue(BLEUUID characteristicUuid); - void setValue(BLEUUID characteristicUuid, std::string value); + BLEClient* getClient(void); // Get a reference to the client associated with this service. + uint16_t getHandle(); // Get the handle of this service. + BLEUUID getUUID(void); // Get the UUID of this service. + std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic. + void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic. std::string toString(void); private: @@ -52,20 +52,23 @@ class BLERemoteService { friend class BLERemoteCharacteristic; // Private methods - void retrieveCharacteristics(void); + void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server. esp_gatt_id_t* getSrvcId(void); - uint16_t getStartHandle(); - uint16_t getEndHandle(); + uint16_t getStartHandle(); // Get the start handle for this service. + uint16_t getEndHandle(); // Get the end handle for this service. + void gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); + void removeCharacteristics(); // Properties // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. std::map m_characteristicMap; + // We maintain a map of characteristics owned by this service keyed by a handle. std::map m_characteristicMapByHandle; @@ -73,9 +76,9 @@ class BLERemoteService { BLEClient* m_pClient; FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); esp_gatt_id_t m_srvcId; - BLEUUID m_uuid; - uint16_t m_startHandle; - uint16_t m_endHandle; + BLEUUID m_uuid; // The UUID of this service. + uint16_t m_startHandle; // The starting handle of this service. + uint16_t m_endHandle; // The ending handle of this service. }; // BLERemoteService #endif /* CONFIG_BT_ENABLED */ From 9b9b24c98abf71131a25e80026ed9a793ca4d287 Mon Sep 17 00:00:00 2001 From: Sepp62 <33748087+Sepp62@users.noreply.github.com> Date: Sun, 3 Dec 2017 23:16:15 +0100 Subject: [PATCH 095/310] Improve stability on BLE notify() - Issue#209 - https://github.com/nkolban/esp32-snippets/issues/209 --- .gitignore | 1 + cpp_utils/BLECharacteristic.cpp | 26 +++++++++++ cpp_utils/BLECharacteristic.h | 6 +-- cpp_utils/BLEServer.cpp | 2 +- cpp_utils/FreeRTOS.cpp | 24 ++++++++-- cpp_utils/FreeRTOS.h | 1 + .../BLETests/Arduino/BLE_uart/BLE_uart.ino | 44 +++++++++++++------ 7 files changed, 84 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 46b14a0a..099e1f7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .project .cproject .settings/ +/esp32-snippets/.vs diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 4c0935a7..7343e89d 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -45,6 +45,7 @@ BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { m_handle = NULL_HANDLE; m_properties = (esp_gatt_char_prop_t)0; m_pCallbacks = nullptr; + m_bConnected = false; setBroadcastProperty((properties & PROPERTY_BROADCAST) !=0); setReadProperty((properties & PROPERTY_READ) !=0); @@ -321,6 +322,7 @@ void BLECharacteristic::handleGATTServerEvent( // - bool need_rsp // case ESP_GATTS_READ_EVT: { + ESP_LOGD(LOG_TAG, "- Testing: 0x%.2x == 0x%.2x", param->read.handle, m_handle); if (param->read.handle == m_handle) { if (m_pCallbacks != nullptr) { @@ -410,6 +412,16 @@ void BLECharacteristic::handleGATTServerEvent( break; } + case ESP_GATTS_CONNECT_EVT: + m_semaphoreConfEvt.give(); + m_bConnected = true; + break; + + case ESP_GATTS_DISCONNECT_EVT: + m_semaphoreConfEvt.give(); + m_bConnected = false; + break; + default: { break; } // default @@ -518,6 +530,8 @@ void BLECharacteristic::notify() { length = 20; } + m_semaphoreConfEvt.take("notify"); + esp_err_t errRc = ::esp_ble_gatts_send_indicate( getService()->getServer()->getGattsIf(), getService()->getServer()->getConnId(), @@ -527,6 +541,8 @@ void BLECharacteristic::notify() { return; } + m_semaphoreConfEvt.wait("notify"); + ESP_LOGD(LOG_TAG, "<< notify"); } // Notify @@ -675,6 +691,16 @@ void BLECharacteristic::setWriteProperty(bool value) { } } // setWriteProperty + /** + * @brief, non-blocking check, whether API is ready to receive data + * @return true - if connected and sempahore is not yet taken + */ +bool BLECharacteristic::isReadyForData() +{ + bool bTaken = m_semaphoreConfEvt.isTaken(); + ESP_LOGV(LOG_TAG, " = %d", !bTaken && m_bConnected); + return !bTaken && m_bConnected; +} /** * @brief Return a string representation of the characteristic. diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 09301834..f24cb6ab 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -63,11 +63,10 @@ class BLECharacteristic { BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); //size_t getLength(); BLEUUID getUUID(); - + std::string getValue(); void indicate(); void notify(); - std::string getValue(); void setBroadcastProperty(bool value); void setCallbacks(BLECharacteristicCallbacks* pCallbacks); void setIndicateProperty(bool value); @@ -79,7 +78,7 @@ class BLECharacteristic { void setWriteNoResponseProperty(bool value); std::string toString(); uint16_t getHandle(); - + bool isReadyForData(); static const uint32_t PROPERTY_READ = 1<<0; static const uint32_t PROPERTY_WRITE = 1<<1; @@ -101,6 +100,7 @@ class BLECharacteristic { BLECharacteristicCallbacks* m_pCallbacks; BLEService* m_pService; BLEValue m_value; + volatile bool m_bConnected; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 448c8031..ce635d98 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -236,7 +236,7 @@ void BLEServer::handleGATTServerEvent( if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. m_pServerCallbacks->onDisconnect(this); } - startAdvertising(); + // startAdvertising(); - do this with some delay from the loop() break; } // ESP_GATTS_DISCONNECT_EVT diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 92390424..0d59ed6a 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -133,9 +133,9 @@ void FreeRTOS::Semaphore::give() { } else { xSemaphoreGive(m_semaphore); } -#ifdef ARDUINO_ARCH_ESP32 - FreeRTOS::sleep(10); -#endif +// #ifdef ARDUINO_ARCH_ESP32 +// FreeRTOS::sleep(10); +// #endif m_owner = std::string(""); } // Semaphore::give @@ -200,6 +200,24 @@ void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take +/** + * @brief non blocking check, if a semaphore is already taken by another thread + * @return true - if sempahore has already been taken by another thread. + */ +bool FreeRTOS::Semaphore::isTaken() { + bool bTaken = true; + ESP_LOGV(LOG_TAG, "Semaphore taken check" ); + + if (m_usePthreads) { + assert(false); + ESP_LOGV(LOG_TAG, "Semaphore::IsTaken illegal mode usePthreads"); + } else { + if( ( bTaken = xSemaphoreTake(m_semaphore, 0) ) ) + xSemaphoreGive( m_semaphore ); + ESP_LOGV(LOG_TAG, "Semaphore taken: %d", !bTaken); + } + return !bTaken; +} // Semaphore::isTaken std::string FreeRTOS::Semaphore::toString() { std::stringstream stringStream; diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index 43a3b8f4..ae960d39 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -40,6 +40,7 @@ class FreeRTOS { void take(uint32_t timeoutMs, std::string owner=""); std::string toString(); uint32_t wait(std::string owner=""); + bool isTaken(); private: SemaphoreHandle_t m_semaphore; diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino index a348a666..4cbb4a52 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino @@ -24,8 +24,11 @@ #include #include -BLECharacteristic *pCharacteristic; +BLEServer *pServer = NULL; +BLECharacteristic *pTxCharacteristic; +BLECharacteristic *pRxCharacteristic; bool deviceConnected = false; +bool oldDeviceConnected = false; uint8_t txValue = 0; // See the following for generating UUIDs: @@ -70,26 +73,26 @@ void setup() { BLEDevice::init("UART Service"); // Create the BLE Server - BLEServer *pServer = BLEDevice::createServer(); + pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service BLEService *pService = pServer->createService(SERVICE_UUID); // Create a BLE Characteristic - pCharacteristic = pService->createCharacteristic( + pTxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY ); - pCharacteristic->addDescriptor(new BLE2902()); + pTxCharacteristic->addDescriptor(new BLE2902()); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( + pRxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE ); - pCharacteristic->setCallbacks(new MyCallbacks()); + pRxCharacteristic->setCallbacks(new MyCallbacks()); // Start the service pService->start(); @@ -101,11 +104,26 @@ void setup() { void loop() { - if (deviceConnected) { - Serial.printf("*** Sent Value: %d ***\n", txValue); - pCharacteristic->setValue(&txValue, 1); - pCharacteristic->notify(); - txValue++; - } - delay(1000); + if (pTxCharacteristic->isReadyForData()) + { + pTxCharacteristic->setValue(&txValue, 1); + pTxCharacteristic->notify(); + txValue++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) + { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) + { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } } From 31860e4c992df3f92ebdf8460fd29e2d7c879018 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 3 Dec 2017 16:53:12 -0600 Subject: [PATCH 096/310] Code changes for #244 --- cpp_utils/TFTP.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/cpp_utils/TFTP.cpp b/cpp_utils/TFTP.cpp index 4e395591..d2c0254b 100644 --- a/cpp_utils/TFTP.cpp +++ b/cpp_utils/TFTP.cpp @@ -114,18 +114,24 @@ void TFTP::TFTP_Transaction::processRRQ() { return; } - uint8_t buf[TFTP_DATA_SIZE + 2 + 2]; // Buffer data size is packet size (512) + 2 bytes for opcode + 2 bytes for blocknumber. + struct { + uint16_t opCode; + uint16_t blockNumber; + uint8_t buf[TFTP_DATA_SIZE]; + } record; + + record.opCode = htons(TFTP_OPCODE_DATA); // Set the op code to be DATA. - *(uint16_t *)(&buf[0]) = htons(TFTP_OPCODE_DATA); // Set the op code to be DATA. while(!finished) { - *(uint16_t *)(&buf[2]) = htons(blockNumber); - int sizeRead = fread(&buf[4], 1, TFTP_DATA_SIZE, file); + record.blockNumber = htons(blockNumber); + + int sizeRead = fread(record.buf, 1, TFTP_DATA_SIZE, file); ESP_LOGD(tag, "Sending data to %s, blockNumber=%d, size=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber, sizeRead); - m_partnerSocket.sendTo(buf, sizeRead+4, &m_partnerAddress); + m_partnerSocket.sendTo((uint8_t*)&record, sizeRead+4, &m_partnerAddress); if (sizeRead < TFTP_DATA_SIZE) { @@ -327,17 +333,20 @@ uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { * RRQ/ | 01/02 | Filename | 0 | Mode | 0 | * WRQ ----------------------------------------------- */ - uint8_t buf[TFTP_DATA_SIZE]; + union { + uint8_t buf[TFTP_DATA_SIZE]; + uint16_t opCode; + } record; size_t length = 100; ESP_LOGD(tag, "TFTP: Waiting for a request"); - pServerSocket->receiveFrom(buf, length, &m_partnerAddress); + pServerSocket->receiveFrom(record.buf, length, &m_partnerAddress); // Save the filename, mode and op code. - m_filename = std::string((char *)(buf+2)); - m_mode = std::string((char *)(buf + 3 + m_filename.length())); - m_opCode = ntohs(*(uint16_t *)buf); + m_filename = std::string((char *)(record.buf + 2)); + m_mode = std::string((char *)(record.buf + 3 + m_filename.length())); + m_opCode = ntohs(record.opCode); switch(m_opCode) { // Handle the Write Request command. From e86cb07a4d2d585ddc68fe68c3d00e7ed46d8bf7 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 5 Dec 2017 19:48:05 -0600 Subject: [PATCH 097/310] Fixes for #245 --- networking/bootwifi/BootWiFi.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index f56016b8..eaf1a4b3 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "BootWiFi.h" #include "sdkconfig.h" @@ -201,7 +202,7 @@ static void processForm(HttpRequest* pRequest, HttpResponse* pResponse) { //pResponse->sendData(std::string((char*)selectAP_html, selectAP_html_len)); pResponse->close(); FreeRTOS::sleep(500); - GeneralUtils::restart(); + System::restart(); ESP_LOGD(LOG_TAG, "<< processForm"); } // processForm From 6e6252d61e4c3b9879629b13a7a5a4790c7e5acc Mon Sep 17 00:00:00 2001 From: Sepp62 <33748087+Sepp62@users.noreply.github.com> Date: Wed, 6 Dec 2017 20:28:58 +0100 Subject: [PATCH 098/310] Improve stability on BLE notify() - Issue#209 - https://github.com/nkolban/esp32-snippets/issues/209 --- .../Arduino/BLE_notify/BLE_notify.ino | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino index 8d329c20..5142c7ce 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino @@ -23,8 +23,10 @@ #include #include +BLEServer *pServer = NULL; BLECharacteristic *pCharacteristic; bool deviceConnected = false; +bool oldDeviceConnected = false; uint8_t value = 0; // See the following for generating UUIDs: @@ -53,7 +55,7 @@ void setup() { BLEDevice::init("MyESP32"); // Create the BLE Server - BLEServer *pServer = BLEDevice::createServer(); + pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service @@ -81,13 +83,23 @@ void setup() { } void loop() { - - if (deviceConnected) { - Serial.printf("*** NOTIFY: %d ***\n", value); - pCharacteristic->setValue(&value, 1); - pCharacteristic->notify(); - //pCharacteristic->indicate(); - value++; - } - delay(2000); + // notify changed value + if (pCharacteristic->isReadyForData()) { + pCharacteristic->setValue(&value, 1); + pCharacteristic->notify(); + value++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } } \ No newline at end of file From ea38814083f497b5ac5af4ffd2f40ff0464b8050 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 7 Dec 2017 20:44:20 -0600 Subject: [PATCH 099/310] Fixes for for #248 --- cpp_utils/JSON.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 72a8da70..5cf1e1ed 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -97,7 +97,7 @@ void JsonArray::addDouble(double value) { * @param [in] value The int value to add to the array. */ void JsonArray::addInt(int value) { - cJSON_AddItemToArray(m_node, cJSON_CreateDouble((double)value, value)); + cJSON_AddItemToArray(m_node, cJSON_CreateNumber((double)value)); } // addInt @@ -326,7 +326,7 @@ void JsonObject::setDouble(std::string name, double value) { * @return N/A. */ void JsonObject::setInt(std::string name, int value) { - cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateDouble((double)value, value)); + cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateNumber((double)value)); } // setInt From b74fcb065f147563f062b78db0a3bbe78c52b965 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 10 Dec 2017 12:37:32 -0600 Subject: [PATCH 100/310] Fixes for #252 --- cpp_utils/HttpParser.cpp | 7 +++--- cpp_utils/HttpResponse.cpp | 22 ++++++++++++++++++- cpp_utils/HttpResponse.h | 1 + cpp_utils/HttpServer.cpp | 45 +++++++++++++++++++++++++++++++++----- cpp_utils/HttpServer.h | 21 ++++++++++-------- 5 files changed, 77 insertions(+), 19 deletions(-) diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index 9594316a..ab5a0b31 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -120,12 +120,9 @@ std::pair parseHeader(std::string &line) { HttpParser::HttpParser() { - // TODO Auto-generated constructor stub - } HttpParser::~HttpParser() { - // TODO Auto-generated destructor stub } @@ -191,6 +188,7 @@ bool HttpParser::hasHeader(const std::string& name) { * @param [in] s The socket from which to retrieve data. */ void HttpParser::parse(Socket s) { + ESP_LOGD(LOG_TAG, ">> parse: socket: %s", s.toString().c_str()); std::string line; line = s.readToDelim(lineTerminator); parseRequestLine(line); @@ -201,6 +199,7 @@ void HttpParser::parse(Socket s) { } // Only PUT and POST requests have a body if (getMethod() != "POST" && getMethod() != "PUT") { + ESP_LOGD(LOG_TAG, "<< parse"); return; } @@ -218,7 +217,7 @@ void HttpParser::parse(Socket s) { int rc = s.receive(data, sizeof(data)); m_body = std::string((char *)data, rc); } - ESP_LOGD(LOG_TAG, "Size of body: %d", m_body.length()); + ESP_LOGD(LOG_TAG, "<< parse: Size of body: %d", m_body.length()); } // parse diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index 70182ca0..b0c60863 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -88,9 +88,10 @@ std::map HttpResponse::getHeaders() { * @param [in] data The data to send to the partner. */ void HttpResponse::sendData(std::string data) { + ESP_LOGD(LOG_TAG, ">> sendData"); // If the request is already closed, nothing further to do. if (m_request->isClosed()) { - ESP_LOGE(LOG_TAG, "Request to send more data but the request/response is already closed"); + ESP_LOGE(LOG_TAG, "<< sendData: Request to send more data but the request/response is already closed"); return; } @@ -101,8 +102,26 @@ void HttpResponse::sendData(std::string data) { // Send the payload data. m_request->getSocket().send(data); + ESP_LOGE(LOG_TAG, "<< sendData"); } // sendData +void HttpResponse::sendData(uint8_t* pData, size_t size) { + ESP_LOGD(LOG_TAG, ">> sendData: 0x%x, size: %d", (uint32_t) pData, size); + // If the request is already closed, nothing further to do. + if (m_request->isClosed()) { + ESP_LOGE(LOG_TAG, "<< sendData: Request to send more data but the request/response is already closed"); + return; + } + + // If we haven't yet sent the header of the data, send that now. + if (m_headerCommitted == false) { + sendHeader(); + } + + // Send the payload data. + m_request->getSocket().send(pData, size); + ESP_LOGD(LOG_TAG, "<< sendData"); +} // sendData /** * @brief Send the header @@ -138,3 +157,4 @@ void HttpResponse::setStatus(const int status, const std::string message) { m_statusMessage = message; } // setStatus + diff --git a/cpp_utils/HttpResponse.h b/cpp_utils/HttpResponse.h index 5c8c5ec0..9e2b4067 100644 --- a/cpp_utils/HttpResponse.h +++ b/cpp_utils/HttpResponse.h @@ -45,6 +45,7 @@ class HttpResponse { std::string getHeader(std::string name); // Get a named header. std::map getHeaders(); // Get all headers. void sendData(std::string data); // Send data to the client. + void sendData(uint8_t* pData, size_t size); // Send data to the client. void setStatus(int status, std::string message); // Set the response status. }; diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 298ab0c3..666e2fc9 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -66,10 +66,11 @@ static void listDirectory(std::string path, HttpResponse& response) { * Constructor for HTTP Server */ HttpServer::HttpServer() { + m_fileBufferSize = 4*1024; // Default size of the file buffer. m_portNumber = 80; // The default port number. m_rootPath = ""; // The default path. m_useSSL = false; // Default SSL is no. - setDirectoryListing(false); // Default directory listing is no. + setDirectoryListing(false); // Default directory listing is disabled. } // HttpServer @@ -100,6 +101,7 @@ class HttpServerTask: public Task { * * If we didn't find a handler, then we are going to behave as a Web Server and try and serve up the * content from the file on the "file system". + * * @param [in] request The HTTP request to process. */ void processRequest(HttpRequest &request) { @@ -134,7 +136,7 @@ class HttpServerTask: public Task { } // Serve up the content from the file on the file system ... if found ... - std::ifstream ifStream; + std::string fileName = m_pHttpServer->getRootPath() + request.getPath(); // Build the absolute file name to read. // If the file name ends with a '/' then remove it ... we are normalizing to NO trailing slashes. @@ -151,6 +153,7 @@ class HttpServerTask: public Task { } // Path was a directory. ESP_LOGD("HttpServerTask", "Opening file: %s", fileName.c_str()); + std::ifstream ifStream; ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. // If we failed to open the requested file, then it probably didn't exist so return a not found. @@ -163,11 +166,16 @@ class HttpServerTask: public Task { } // We now have an open file and want to push the content of that file through to the browser. + // because of defect #252 we have to do some pretty important re-work here. Specifically, we can't host the whole file in + // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. HttpResponse response(&request); response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - std::stringstream ss; - ss << ifStream.rdbuf(); - response.sendData(ss.str()); + uint8_t *pData = new uint8_t[1000]; + while(!ifStream.eof()) { + ifStream.read((char *)pData, 1000); + response.sendData(pData, ifStream.gcount()); + } + delete[] pData; ifStream.close(); } // processRequest @@ -271,6 +279,18 @@ void HttpServer::addPathHandler( } // addPathHandler +/** + * @brief Get the size of the file buffer. + * When serving up a file from the file system, we can't afford to read the whole file into RAM before + * sending it. As such, we must read the file in chunks. The buffer size is the size of a chunk to be + * transmitted before the next chunk is read. + * @return The file buffer size. + */ +size_t HttpServer::getFileBufferSize() { + return m_fileBufferSize; +} // getFileBufferSize + + /** * @brief Get the port number on which the HTTP Server is listening. * @return The port number on which the HTTP server is listening. @@ -303,6 +323,18 @@ void HttpServer::setDirectoryListing(bool use) { } // setDirectoryListening +/** + * @brief Set the size of the file buffer. + * When serving up a file from the file system, we can't afford to read the whole file into RAM before + * sending it. As such, we must read the file in chunks. The buffer size is the size of a chunk to be + * transmitted before the next chunk is read. + * @param [in] fileBufferSize How large should the file buffer size be? + */ +void HttpServer::setFileBufferSize(size_t fileBufferSize) { + m_fileBufferSize = fileBufferSize; +} // setFileBufferSize + + /** * @brief Set the root path for URL file mapping. * @@ -394,6 +426,7 @@ PathHandler::PathHandler(std::string method, std::string matchPath, m_method = method; // Save the method we are looking for. m_textPattern = matchPath; m_isRegex = false; + m_pRegex = nullptr; m_pRequestHandler = pWebServerRequestHandler; // The handler to be invoked if the pattern matches. } // PathHandler @@ -430,3 +463,5 @@ void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse *response } // invokePathHandler + + diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index f07abedd..adf2d573 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -74,23 +74,26 @@ class HttpServer { HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ); - uint16_t getPort(); // Get the port on which the Http server is listening. - std::string getRootPath(); // Get the root of the file system path. - bool getSSL(); // Are we using SSL? - void setDirectoryListing(bool use); // Should we list the content of directories? - void setRootPath(std::string path); // Set the root of the file system path. + size_t getFileBufferSize(); // Get the current size of the file buffer. + uint16_t getPort(); // Get the port on which the Http server is listening. + std::string getRootPath(); // Get the root of the file system path. + bool getSSL(); // Are we using SSL? + void setDirectoryListing(bool use); // Should we list the content of directories? + void setFileBufferSize(size_t fileBufferSize); // Set the size of the file buffer + void setRootPath(std::string path); // Set the root of the file system path. void start(uint16_t portNumber, bool useSSL=false); void stop(); // Stop a previously started server. + private: friend class HttpServerTask; friend class WebSocket; - uint16_t m_portNumber; // Port number on which server is listening. + size_t m_fileBufferSize; // Size of the file buffer. + bool m_directoryListing; // Should we list directory content? std::vector m_pathHandlers; // Vector of path handlers. + uint16_t m_portNumber; // Port number on which server is listening. std::string m_rootPath; // Root path into the file system. - bool m_useSSL; // Is this server listening on an HTTPS port? - bool m_directoryListing; // Should we list directory content? - //SockServ m_sockServ; // Server socket. Socket m_socket; + bool m_useSSL; // Is this server listening on an HTTPS port? }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ From 6558d50fc63c7f982395bfe4d349ce4f636bd315 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 10 Dec 2017 16:13:03 -0600 Subject: [PATCH 101/310] More fixes for #252 --- cpp_utils/HttpServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 666e2fc9..7f4f8b5b 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -170,9 +170,9 @@ class HttpServerTask: public Task { // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. HttpResponse response(&request); response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - uint8_t *pData = new uint8_t[1000]; + uint8_t *pData = new uint8_t[m_pHttpServer->getFileBufferSize()]; while(!ifStream.eof()) { - ifStream.read((char *)pData, 1000); + ifStream.read((char *)pData, m_pHttpServer->getFileBufferSize()); response.sendData(pData, ifStream.gcount()); } delete[] pData; From 94734b54457c534351c6258e6327aed08aa9100c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 10 Dec 2017 23:19:40 -0600 Subject: [PATCH 102/310] Fixes for #256 --- cpp_utils/HttpResponse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index b0c60863..adc72648 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -102,7 +102,7 @@ void HttpResponse::sendData(std::string data) { // Send the payload data. m_request->getSocket().send(data); - ESP_LOGE(LOG_TAG, "<< sendData"); + ESP_LOGD(LOG_TAG, "<< sendData"); } // sendData void HttpResponse::sendData(uint8_t* pData, size_t size) { From d52d5b25cd6e835379bcb31a7b733eaa1b8c7dba Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 11 Dec 2017 00:44:45 -0600 Subject: [PATCH 103/310] Fixes for #257 --- cpp_utils/HttpServer.cpp | 105 +++++++++++++++++++++++---------------- cpp_utils/HttpServer.h | 1 + 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 7f4f8b5b..488f05ce 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -20,47 +20,7 @@ static const char* LOG_TAG = "HttpServer"; #undef close -/** - * Send a directory listing back to the browser. - * @param [in] path The path of the directory to list. - * @param [in] response The response object to use to send data back to the browser. - */ -static void listDirectory(std::string path, HttpResponse& response) { - // If path ends with a "/" then remove it. - if (GeneralUtils::endsWith(path, '/')) { - path = path.substr(0, path.length()-1); - } - response.addHeader("Content-Type", "text/html"); - response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - response.sendData(""); - if (!GeneralUtils::endsWith(path, '/')) { - response.sendData(""); - } - response.sendData(""); - response.sendData("

" + path + "

"); - response.sendData("
"); - response.sendData("

[To Parent Directory]

"); - response.sendData(""); - auto files = FileSystem::getDirectoryContents(path); - for (auto it = files.begin(); it != files.end(); ++it) { - std::stringstream ss; - ss << ""; - if (it->isDirectory()) { - ss << ""; - } - else { - ss << ""; - } - ss << ""; - response.sendData(ss.str()); - ESP_LOGD(LOG_TAG, "file: %s", ss.str().c_str()); - } - response.sendData("
" << it->getName() << "<dir>" << it->length() << "
"); - response.sendData("
"); - response.sendData(""); - response.close(); -} // listDirectory /** * Constructor for HTTP Server @@ -109,7 +69,7 @@ class HttpServerTask: public Task { request.getMethod().c_str(), request.getPath().c_str()); // Loop over all the path handlers we have looking for the first one that matches. Note that none of them - // may match. If we find one that does, then invoke the handler and that is the end of processing. + // need to match. If we find one that does, then invoke the handler and that is the end of processing. for (auto pathHandlerIterartor = m_pHttpServer->m_pathHandlers.begin(); pathHandlerIterartor != m_pHttpServer->m_pathHandlers.end(); ++pathHandlerIterartor) { @@ -148,7 +108,7 @@ class HttpServerTask: public Task { if (FileSystem::isDirectory(fileName)) { ESP_LOGD(LOG_TAG, "Path %s is a directory", fileName.c_str()); HttpResponse response(&request); - listDirectory(fileName, response); + m_pHttpServer->listDirectory(fileName, response); // List the contents of the directory. return; } // Path was a directory. @@ -309,11 +269,66 @@ std::string HttpServer::getRootPath() { } // getRootPath +/** + * @brief Return whether or not we are using SSL. + * @return True if we are using SSL. + */ bool HttpServer::getSSL() { return m_useSSL; } // getSSL +/** + * Send a directory listing back to the browser. + * @param [in] path The path of the directory to list. + * @param [in] response The response object to use to send data back to the browser. + */ +void HttpServer::listDirectory(std::string path, HttpResponse& response) { + // If path ends with a "/" then remove it. + if (GeneralUtils::endsWith(path, '/')) { + path = path.substr(0, path.length()-1); + } + + // The url path may not be the same as path which is the path on the file system. + // They will differ if we are using a root path. We check to see if the file system path + // starts with the root path, if it does, we remove the root path from the url path. + std::string urlPath = path; + if (urlPath.compare(0, getRootPath().length(), getRootPath()) == 0) { + urlPath = urlPath.substr(getRootPath().length()); + } + + response.addHeader("Content-Type", "text/html"); + response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + response.sendData(""); + if (!GeneralUtils::endsWith(path, '/')) { + response.sendData(""); + } + response.sendData(""); + response.sendData("

" + path + "

"); + response.sendData("
"); + response.sendData("

[To Parent Directory]

"); + response.sendData(""); + auto files = FileSystem::getDirectoryContents(path); + for (auto it = files.begin(); it != files.end(); ++it) { + std::stringstream ss; + ss << ""; + if (it->isDirectory()) { + ss << ""; + } + else { + ss << ""; + } + + ss << ""; + response.sendData(ss.str()); + ESP_LOGD(LOG_TAG, "file: %s", ss.str().c_str()); + } + response.sendData("
" << it->getName() << "<dir>" << it->length() << "
"); + response.sendData("
"); + response.sendData(""); + response.close(); +} // listDirectory + /** * @brief Set whether or not we will list directories. * @param [in] use Set to true to enable directory listing. @@ -353,6 +368,12 @@ void HttpServer::setFileBufferSize(size_t fileBufferSize) { * @return N/A. */ void HttpServer::setRootPath(std::string path) { + + // Should the user have supplied a path that ends in a "/" remove the trailing slash. This also + // means that "/" becomes "". + if (GeneralUtils::endsWith(path, '/')) { + path = path.substr(0, path.length()-1); + } m_rootPath = path; } // setRootPath diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index adf2d573..c814d0c8 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -87,6 +87,7 @@ class HttpServer { private: friend class HttpServerTask; friend class WebSocket; + void listDirectory(std::string path, HttpResponse& response); size_t m_fileBufferSize; // Size of the file buffer. bool m_directoryListing; // Should we list directory content? std::vector m_pathHandlers; // Vector of path handlers. From 3d53194a6c4a2133a094b58439f943bb25dba9dd Mon Sep 17 00:00:00 2001 From: boonkerz Date: Tue, 12 Dec 2017 02:46:28 +0100 Subject: [PATCH 104/310] after an start it should be resend the address as example for the bm280: i2cBus.setAddress(0x77); i2cBus.beginTransaction(); i2cBus.write(BME280_CHIP_ID_ADDR, true); i2cBus.start(); i2cBus.read(&chip_id, false); i2cBus.endTransaction(); --- cpp_utils/I2C.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 3aa740c7..aac369cf 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -248,6 +248,7 @@ void I2C::start() { if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + m_directionKnown = false; } // start From 7464995182951b5ef38f515a8d567a2f4afbf985 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 11 Dec 2017 23:38:42 -0600 Subject: [PATCH 105/310] changes for #265 --- cpp_utils/GeneralUtils.cpp | 20 +++++++++++++++++++- cpp_utils/GeneralUtils.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index fd0c740c..5972101f 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include static const char* LOG_TAG = "GeneralUtils"; @@ -98,6 +100,23 @@ bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { } // base64Encode +/** + * @brief Dump general info to the log. + * Data includes: + * * Amount of free RAM + */ +void GeneralUtils::dumpInfo() { + size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + ESP_LOGD(LOG_TAG, "--- dumpInfo ---"); + ESP_LOGD(LOG_TAG, "Free heap: %d", freeHeap); + ESP_LOGD(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); + ESP_LOGD(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); + ESP_LOGD(LOG_TAG, "---"); +} // dumpInfo + + /** * @brief Does the string end with a specific character? * @param [in] str The string to examine. @@ -398,4 +417,3 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { } // errorToString - diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 54aae5e3..05262a25 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -18,6 +18,7 @@ class GeneralUtils { public: static bool base64Decode(const std::string& in, std::string* out); static bool base64Encode(const std::string& in, std::string* out); + static void dumpInfo(); static bool endsWith(std::string str, char c); static const char* errorToString(esp_err_t errCode); static void hexDump(const uint8_t* pData, uint32_t length); From 67331f32bfdc5680f3f4d2594e22633ee2cb8562 Mon Sep 17 00:00:00 2001 From: Sepp62 <33748087+Sepp62@users.noreply.github.com> Date: Wed, 13 Dec 2017 20:44:04 +0100 Subject: [PATCH 106/310] Issue #209 - reverted to single threaded operation --- cpp_utils/BLECharacteristic.cpp | 14 ---------- cpp_utils/BLECharacteristic.h | 2 -- cpp_utils/FreeRTOS.cpp | 19 -------------- cpp_utils/FreeRTOS.h | 1 - .../Arduino/BLE_notify/BLE_notify.ino | 5 ++-- .../BLETests/Arduino/BLE_uart/BLE_uart.ino | 26 ++++++++----------- 6 files changed, 13 insertions(+), 54 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 7343e89d..5754c488 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -45,7 +45,6 @@ BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { m_handle = NULL_HANDLE; m_properties = (esp_gatt_char_prop_t)0; m_pCallbacks = nullptr; - m_bConnected = false; setBroadcastProperty((properties & PROPERTY_BROADCAST) !=0); setReadProperty((properties & PROPERTY_READ) !=0); @@ -414,12 +413,10 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_CONNECT_EVT: m_semaphoreConfEvt.give(); - m_bConnected = true; break; case ESP_GATTS_DISCONNECT_EVT: m_semaphoreConfEvt.give(); - m_bConnected = false; break; default: { @@ -691,17 +688,6 @@ void BLECharacteristic::setWriteProperty(bool value) { } } // setWriteProperty - /** - * @brief, non-blocking check, whether API is ready to receive data - * @return true - if connected and sempahore is not yet taken - */ -bool BLECharacteristic::isReadyForData() -{ - bool bTaken = m_semaphoreConfEvt.isTaken(); - ESP_LOGV(LOG_TAG, " = %d", !bTaken && m_bConnected); - return !bTaken && m_bConnected; -} - /** * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index f24cb6ab..1c59247f 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -78,7 +78,6 @@ class BLECharacteristic { void setWriteNoResponseProperty(bool value); std::string toString(); uint16_t getHandle(); - bool isReadyForData(); static const uint32_t PROPERTY_READ = 1<<0; static const uint32_t PROPERTY_WRITE = 1<<1; @@ -100,7 +99,6 @@ class BLECharacteristic { BLECharacteristicCallbacks* m_pCallbacks; BLEService* m_pService; BLEValue m_value; - volatile bool m_bConnected; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 0d59ed6a..674e6f8e 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -200,25 +200,6 @@ void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take -/** - * @brief non blocking check, if a semaphore is already taken by another thread - * @return true - if sempahore has already been taken by another thread. - */ -bool FreeRTOS::Semaphore::isTaken() { - bool bTaken = true; - ESP_LOGV(LOG_TAG, "Semaphore taken check" ); - - if (m_usePthreads) { - assert(false); - ESP_LOGV(LOG_TAG, "Semaphore::IsTaken illegal mode usePthreads"); - } else { - if( ( bTaken = xSemaphoreTake(m_semaphore, 0) ) ) - xSemaphoreGive( m_semaphore ); - ESP_LOGV(LOG_TAG, "Semaphore taken: %d", !bTaken); - } - return !bTaken; -} // Semaphore::isTaken - std::string FreeRTOS::Semaphore::toString() { std::stringstream stringStream; stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index ae960d39..43a3b8f4 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -40,7 +40,6 @@ class FreeRTOS { void take(uint32_t timeoutMs, std::string owner=""); std::string toString(); uint32_t wait(std::string owner=""); - bool isTaken(); private: SemaphoreHandle_t m_semaphore; diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino index 5142c7ce..57ad7a7d 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino @@ -24,7 +24,6 @@ #include BLEServer *pServer = NULL; -BLECharacteristic *pCharacteristic; bool deviceConnected = false; bool oldDeviceConnected = false; uint8_t value = 0; @@ -62,7 +61,7 @@ void setup() { BLEService *pService = pServer->createService(SERVICE_UUID); // Create a BLE Characteristic - pCharacteristic = pService->createCharacteristic( + BLECharacteristic * pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | @@ -84,7 +83,7 @@ void setup() { void loop() { // notify changed value - if (pCharacteristic->isReadyForData()) { + if (deviceConnected) { pCharacteristic->setValue(&value, 1); pCharacteristic->notify(); value++; diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino index 4cbb4a52..35b570b9 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino @@ -25,8 +25,7 @@ #include BLEServer *pServer = NULL; -BLECharacteristic *pTxCharacteristic; -BLECharacteristic *pRxCharacteristic; +BLECharacteristic * pTxCharacteristic; bool deviceConnected = false; bool oldDeviceConnected = false; uint8_t txValue = 0; @@ -81,16 +80,16 @@ void setup() { // Create a BLE Characteristic pTxCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_TX, - BLECharacteristic::PROPERTY_NOTIFY - ); + CHARACTERISTIC_UUID_TX, + BLECharacteristic::PROPERTY_NOTIFY + ); pTxCharacteristic->addDescriptor(new BLE2902()); - pRxCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_RX, - BLECharacteristic::PROPERTY_WRITE - ); + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + BLECharacteristic::PROPERTY_WRITE + ); pRxCharacteristic->setCallbacks(new MyCallbacks()); @@ -104,8 +103,7 @@ void setup() { void loop() { - if (pTxCharacteristic->isReadyForData()) - { + if (deviceConnected) { pTxCharacteristic->setValue(&txValue, 1); pTxCharacteristic->notify(); txValue++; @@ -113,16 +111,14 @@ void loop() { } // disconnecting - if (!deviceConnected && oldDeviceConnected) - { + if (!deviceConnected && oldDeviceConnected) { delay(500); // give the bluetooth stack the chance to get things ready pServer->startAdvertising(); // restart advertising Serial.println("start advertising"); oldDeviceConnected = deviceConnected; } // connecting - if (deviceConnected && !oldDeviceConnected) - { + if (deviceConnected && !oldDeviceConnected) { // do stuff here on connecting oldDeviceConnected = deviceConnected; } From 2adbc1cb90e3dd567a3a0c80f7fed617400c065c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 13 Dec 2017 20:08:43 -0600 Subject: [PATCH 107/310] Fixes for #276 --- cpp_utils/BLEServer.cpp | 2 +- cpp_utils/GeneralUtils.cpp | 26 +++++++++++++++++++++++ cpp_utils/GeneralUtils.h | 3 +++ cpp_utils/HttpParser.cpp | 42 ++++++++++++++++++-------------------- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index ce635d98..87c012b3 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -236,7 +236,7 @@ void BLEServer::handleGATTServerEvent( if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. m_pServerCallbacks->onDisconnect(this); } - // startAdvertising(); - do this with some delay from the loop() + startAdvertising(); //- do this with some delay from the loop() break; } // ESP_GATTS_DISCONNECT_EVT diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 5972101f..42ea682f 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -417,3 +417,29 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { } // errorToString +/** + * @brief Convert a string to lower case. + * @param [in] value The string to convert to lower case. + * @return A lower case representation of the string. + */ +std::string GeneralUtils::toLower(std::string& value) { + // Question: Could this be improved with a signature of: + // std::string& GeneralUtils::toLower(std::string& value) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + return value; +} // toLower + + +/** + * @brief Remove white space from a string. + */ +std::string GeneralUtils::trim(const std::string& str) +{ + size_t first = str.find_first_not_of(' '); + if (std::string::npos == first) + { + return str; + } + size_t last = str.find_last_not_of(' '); + return str.substr(first, (last - first + 1)); +} // trim diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 05262a25..9f945071 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -10,6 +10,7 @@ #include #include #include +#include /** * @brief General utilities. @@ -23,6 +24,8 @@ class GeneralUtils { static const char* errorToString(esp_err_t errCode); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); + static std::string toLower(std::string& value); + static std::string trim(const std::string& str); }; diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index ab5a0b31..9eb6d535 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -10,6 +10,7 @@ #include #include "HttpParser.h" #include "HttpRequest.h" +#include "GeneralUtils.h" #include @@ -35,21 +36,6 @@ static const char* LOG_TAG = "HttpParser"; static std::string lineTerminator = "\r\n"; -/** - * @brief Remove white space from a string. - */ -static std::string trim(const std::string& str) -{ - size_t first = str.find_first_not_of(' '); - if (std::string::npos == first) - { - return str; - } - size_t last = str.find_last_not_of(' '); - return str.substr(first, (last - first + 1)); -} // trim - - /** * @brief Parse an incoming line of text until we reach a delimiter. * @param [in/out] it The current iterator in the text input. @@ -113,8 +99,10 @@ static std::string toCharToken(std::string::iterator &it, std::string &str, char */ std::pair parseHeader(std::string &line) { auto it = line.begin(); - auto name = toCharToken(it, line, ':'); - auto value = trim(toStringToken(it, line, lineTerminator)); + std::string name = toCharToken(it, line, ':'); // Parse the line until we find a ':' + // We normalize the header name to be lower case. + GeneralUtils::toLower(name); + auto value = GeneralUtils::trim(toStringToken(it, line, lineTerminator)); return std::pair(name, value); } // parseHeader @@ -145,17 +133,25 @@ std::string HttpParser::getBody() { } +/** + * @brief Retrieve the value of the named header. + * @param [in] name The name of the header to retrieve. + * @return The value of the named header or null if not present. + */ std::string HttpParser::getHeader(const std::string& name) { - if (!hasHeader(name)) { + // We normalize the header name to be lower case. + std::string localName = name; + GeneralUtils::toLower(localName); + if (!hasHeader(localName)) { return ""; } - return m_headers.at(name); -} + return m_headers.at(localName); +} // getHeader std::map HttpParser::getHeaders() { return m_headers; -} +} // getHeaders std::string HttpParser::getMethod() { @@ -179,7 +175,9 @@ std::string HttpParser::getVersion() { * @return True if the header is present and false otherwise. */ bool HttpParser::hasHeader(const std::string& name) { - return m_headers.find(name) != m_headers.end(); + // We normalize the header name to be lower case. + std::string localName = name; + return m_headers.find(GeneralUtils::toLower(localName)) != m_headers.end(); } // hasHeader From e71adf0426f9c204946de67f1882f88b24cb4f8b Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 13 Dec 2017 23:40:52 -0600 Subject: [PATCH 108/310] Fixes for #275 --- cpp_utils/BLEClient.cpp | 2 +- cpp_utils/BLEDevice.cpp | 2 +- cpp_utils/BLEDevice.h | 2 +- cpp_utils/BLEScan.cpp | 11 ++++++++--- cpp_utils/BLEScan.h | 5 ++++- cpp_utils/BLEServer.cpp | 2 +- cpp_utils/BLEUtils.cpp | 2 +- cpp_utils/tests/BLETests/SampleWrite.cpp | 2 ++ 8 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 189d5d74..f3987d08 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -7,7 +7,7 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include -#include +#include #include #include #include diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 26b4203e..b89711a1 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -11,7 +11,7 @@ #include #include #include -#include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 5399ff7c..ecf5e6c7 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -13,7 +13,7 @@ #include // ESP32 BLE #include // Part of C++ STL #include -#include +#include #include "BLEServer.h" #include "BLEClient.h" diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 7c5b7bd9..e450664e 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -33,6 +33,7 @@ BLEScan::BLEScan() { m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; m_pAdvertisedDeviceCallbacks = nullptr; m_stopped = true; + m_wantDuplicates = false; setInterval(100); setWindow(100); } // BLEScan @@ -97,7 +98,7 @@ void BLEScan::handleGAPEvent( break; } } - if (found) { + if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str()); break; } @@ -115,7 +116,9 @@ void BLEScan::handleGAPEvent( m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); } - m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + } break; } // ESP_GAP_SEARCH_INQ_RES_EVT @@ -154,8 +157,10 @@ void BLEScan::setActiveScan(bool active) { /** * @brief Set the call backs to be invoked. * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. */ -void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks) { +void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { + m_wantDuplicates = wantDuplicates; m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; } // setAdvertisedDeviceCallbacks diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index edf79a9a..c905c436 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -48,7 +48,9 @@ class BLEScanResults { class BLEScan { public: void setActiveScan(bool active); - void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks); + void setAdvertisedDeviceCallbacks( + BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates = false); void setInterval(uint16_t intervalMSecs); void setWindow(uint16_t windowMSecs); BLEScanResults start(uint32_t duration); @@ -68,6 +70,7 @@ class BLEScan { bool m_stopped; FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); BLEScanResults m_scanResults; + bool m_wantDuplicates; }; // BLEScan #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 87c012b3..75805135 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -8,7 +8,7 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include -#include +#include #include #include #include diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index d8d318f2..f96db1c5 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -14,7 +14,7 @@ #include #include -#include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE diff --git a/cpp_utils/tests/BLETests/SampleWrite.cpp b/cpp_utils/tests/BLETests/SampleWrite.cpp index 2832f9ec..de8f251a 100644 --- a/cpp_utils/tests/BLETests/SampleWrite.cpp +++ b/cpp_utils/tests/BLETests/SampleWrite.cpp @@ -11,6 +11,7 @@ #include #include #include "BLEDevice.h" +#include "GeneralUtils.h" #include "sdkconfig.h" @@ -35,6 +36,7 @@ class MyCallbacks: public BLECharacteristicCallbacks { static void run() { + GeneralUtils::dumpInfo(); BLEDevice::init("MYDEVICE"); BLEServer *pServer = BLEDevice::createServer(); From e36ba9778cdf46aea5c3419dae61a13da3cec6c9 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 14 Dec 2017 19:58:31 -0600 Subject: [PATCH 109/310] Fixes for #280 --- cpp_utils/BLEScan.cpp | 2 ++ cpp_utils/WiFi.cpp | 15 ++++++++++++++- cpp_utils/WiFi.h | 2 +- cpp_utils/WiFiEventHandler.cpp | 12 ++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index e450664e..e20c7e09 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -105,6 +105,8 @@ void BLEScan::handleGAPEvent( // We now construct a model of the advertised device that we have just found for the first // time. + FreeRTOS::sleep(50); + break; BLEAdvertisedDevice advertisedDevice; advertisedDevice.setAddress(advertisedAddress); advertisedDevice.setRSSI(param->scan_rst.rssi); diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 4054dfe5..d8ba2924 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -482,9 +482,16 @@ std::vector WiFi::scan() { * * @param[in] ssid The SSID to use to advertize for stations. * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP * @return N/A. */ -void WiFi::startAP(const std::string& ssid, const std::string& password) { +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); init(); @@ -518,6 +525,12 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + + errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< startAP"); } // startAP diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index e9af3d67..eec479d2 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -138,7 +138,7 @@ class WiFi { static std::string getStaMac(); static std::string getStaSSID(); std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); void setIPInfo(const char* ip, const char* gw, const char* netmask); void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); diff --git a/cpp_utils/WiFiEventHandler.cpp b/cpp_utils/WiFiEventHandler.cpp index 405058ad..319e2f25 100644 --- a/cpp_utils/WiFiEventHandler.cpp +++ b/cpp_utils/WiFiEventHandler.cpp @@ -37,10 +37,22 @@ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { rc = pWiFiEventHandler->apStart(); break; } + case SYSTEM_EVENT_AP_STOP: { rc = pWiFiEventHandler->apStop(); break; } + + case SYSTEM_EVENT_AP_STACONNECTED: { + rc = pWiFiEventHandler->apStaConnected(); + break; + } + + case SYSTEM_EVENT_AP_STADISCONNECTED: { + rc = pWiFiEventHandler->apStaDisconnected(); + break; + } + case SYSTEM_EVENT_STA_CONNECTED: { rc = pWiFiEventHandler->staConnected(); break; From 1e4c5afcacf6d2318799413c7d343ce6345ae587 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 14 Dec 2017 19:59:49 -0600 Subject: [PATCH 110/310] Fixes for #280 --- cpp_utils/BLEScan.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index e20c7e09..e450664e 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -105,8 +105,6 @@ void BLEScan::handleGAPEvent( // We now construct a model of the advertised device that we have just found for the first // time. - FreeRTOS::sleep(50); - break; BLEAdvertisedDevice advertisedDevice; advertisedDevice.setAddress(advertisedAddress); advertisedDevice.setRSSI(param->scan_rst.rssi); From af58387668375e8620c78e44f55e97f42f0f88e7 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 14 Dec 2017 20:19:20 -0600 Subject: [PATCH 111/310] Fixes for #263 --- cpp_utils/BLECharacteristic.h | 1 + cpp_utils/BLEDescriptor.h | 1 + cpp_utils/BLEService.h | 5 ++--- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 1c59247f..7bb54c64 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -87,6 +87,7 @@ class BLECharacteristic { static const uint32_t PROPERTY_WRITE_NR = 1<<5; private: + friend class BLEServer; friend class BLEService; friend class BLEDescriptor; diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 4eda4c91..ef716d42 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -42,6 +42,7 @@ class BLEDescriptor { std::string toString(); private: + friend class BLEDescriptorMap; friend class BLECharacteristic; BLEUUID m_bleUUID; diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index fdba54fd..2b78e620 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -52,9 +52,6 @@ class BLECharacteristicMap { */ class BLEService { public: - BLEService(const char* uuid, uint32_t numHandles=10); - BLEService(BLEUUID uuid, uint32_t numHandles=10); - void addCharacteristic(BLECharacteristic* pCharacteristic); BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); @@ -69,6 +66,8 @@ class BLEService { uint16_t getHandle(); private: + BLEService(const char* uuid, uint32_t numHandles); + BLEService(BLEUUID uuid, uint32_t numHandles); friend class BLEServer; friend class BLEServiceMap; friend class BLEDescriptor; From 4cd4ed193961ec23011d0abfdd9be84241eaa9fe Mon Sep 17 00:00:00 2001 From: anio Date: Fri, 15 Dec 2017 15:09:05 +0200 Subject: [PATCH 112/310] Issue #280 fix auth --- cpp_utils/WiFi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index d8ba2924..25f9d1b5 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -509,7 +509,7 @@ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_au apConfig.ap.ssid_len = ssid.size(); ::memcpy(apConfig.ap.password, password.data(), password.size()); apConfig.ap.channel = 0; - apConfig.ap.authmode = WIFI_AUTH_OPEN; + apConfig.ap.authmode = auth; apConfig.ap.ssid_hidden = 0; apConfig.ap.max_connection = 4; apConfig.ap.beacon_interval = 100; From 4931b218033b09d6ee29f5c96a7978fc1c94a9aa Mon Sep 17 00:00:00 2001 From: anio Date: Fri, 15 Dec 2017 16:58:25 +0200 Subject: [PATCH 113/310] Extend WiFiEventHandler. Add info arguments. --- cpp_utils/NeoPixelWiFiEventHandler.cpp | 6 +- cpp_utils/NeoPixelWiFiEventHandler.h | 6 +- cpp_utils/WiFiEventHandler.cpp | 76 +++++++++++++++++++++++--- cpp_utils/WiFiEventHandler.h | 46 +++++++++++----- cpp_utils/cpp_utils | 1 - 5 files changed, 106 insertions(+), 29 deletions(-) delete mode 120000 cpp_utils/cpp_utils diff --git a/cpp_utils/NeoPixelWiFiEventHandler.cpp b/cpp_utils/NeoPixelWiFiEventHandler.cpp index 9206b567..1632a0f7 100644 --- a/cpp_utils/NeoPixelWiFiEventHandler.cpp +++ b/cpp_utils/NeoPixelWiFiEventHandler.cpp @@ -23,14 +23,14 @@ esp_err_t NeoPixelWiFiEventHandler::apStart() { return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staConnected() { +esp_err_t NeoPixelWiFiEventHandler::staConnected(system_event_sta_connected_t info) { printf("XXX staConnected\n"); ws2812->setPixel(0, 57, 89, 66); ws2812->show(); return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staDisconnected() { +esp_err_t NeoPixelWiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { printf("XXX staDisconnected\n"); ws2812->setPixel(0, 64, 0, 0); ws2812->show(); @@ -44,7 +44,7 @@ esp_err_t NeoPixelWiFiEventHandler::staStart() { return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { +esp_err_t NeoPixelWiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { printf("XXX staGotIp\n"); ws2812->setPixel(0, 0, 64, 0); ws2812->show(); diff --git a/cpp_utils/NeoPixelWiFiEventHandler.h b/cpp_utils/NeoPixelWiFiEventHandler.h index c271e3e3..3e9b596e 100644 --- a/cpp_utils/NeoPixelWiFiEventHandler.h +++ b/cpp_utils/NeoPixelWiFiEventHandler.h @@ -23,9 +23,9 @@ class NeoPixelWiFiEventHandler: public WiFiEventHandler { virtual ~NeoPixelWiFiEventHandler(); esp_err_t apStart() override; - esp_err_t staConnected() override; - esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) override; - esp_err_t staDisconnected() override; + esp_err_t staConnected(system_event_sta_connected_t info) override; + esp_err_t staGotIp(system_event_sta_got_ip_t info) override; + esp_err_t staDisconnected(system_event_sta_disconnected_t info) override; esp_err_t wifiReady() override; esp_err_t staStart() override; private: diff --git a/cpp_utils/WiFiEventHandler.cpp b/cpp_utils/WiFiEventHandler.cpp index 319e2f25..c5a4fa2f 100644 --- a/cpp_utils/WiFiEventHandler.cpp +++ b/cpp_utils/WiFiEventHandler.cpp @@ -44,22 +44,32 @@ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { } case SYSTEM_EVENT_AP_STACONNECTED: { - rc = pWiFiEventHandler->apStaConnected(); + rc = pWiFiEventHandler->apStaConnected(event->event_info.sta_connected); break; } case SYSTEM_EVENT_AP_STADISCONNECTED: { - rc = pWiFiEventHandler->apStaDisconnected(); + rc = pWiFiEventHandler->apStaDisconnected(event->event_info.sta_disconnected); + break; + } + + case SYSTEM_EVENT_SCAN_DONE: { + rc = pWiFiEventHandler->staScanDone(event->event_info.scan_done); + break; + } + + case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { + rc = pWiFiEventHandler->staAuthChange(event->event_info.auth_change); break; } case SYSTEM_EVENT_STA_CONNECTED: { - rc = pWiFiEventHandler->staConnected(); + rc = pWiFiEventHandler->staConnected(event->event_info.connected); break; } case SYSTEM_EVENT_STA_DISCONNECTED: { - rc = pWiFiEventHandler->staDisconnected(); + rc = pWiFiEventHandler->staDisconnected(event->event_info.disconnected); break; } @@ -123,7 +133,7 @@ system_event_cb_t WiFiEventHandler::getEventHandler() { * @param [in] event_sta_got_ip The Station Got IP event. * @return An indication of whether or not we processed the event successfully. */ -esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { +esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { ESP_LOGD(LOG_TAG, "default staGotIp"); return ESP_OK; } // staGotIp @@ -169,29 +179,77 @@ esp_err_t WiFiEventHandler::staStop() { } // staStop -esp_err_t WiFiEventHandler::staConnected() { +/** + * @brief Handle the Station Connected event. + * Handle having connected to remote AP. + * @param [in] event_connected system_event_sta_connected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staConnected(system_event_sta_connected_t info) { ESP_LOGD(LOG_TAG, "default staConnected"); return ESP_OK; } // staConnected -esp_err_t WiFiEventHandler::staDisconnected() { +/** + * @brief Handle the Station Disconnected event. + * Handle having disconnected from remote AP. + * @param [in] event_disconnected system_event_sta_disconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { ESP_LOGD(LOG_TAG, "default staDisconnected"); return ESP_OK; } // staDisconnected -esp_err_t WiFiEventHandler::apStaConnected() { +/** + * @brief Handle a Station Connected to AP event. + * Handle having a station connected to ESP32 soft-AP. + * @param [in] event_sta_connected system_event_ap_staconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::apStaConnected(system_event_ap_staconnected_t info) { ESP_LOGD(LOG_TAG, "default apStaConnected"); return ESP_OK; } // apStaConnected -esp_err_t WiFiEventHandler::apStaDisconnected() { +/** + * @brief Handle a Station Disconnected from AP event. + * Handle having a station disconnected from ESP32 soft-AP. + * @param [in] event_sta_disconnected system_event_ap_stadisconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::apStaDisconnected(system_event_ap_stadisconnected_t info) { ESP_LOGD(LOG_TAG, "default apStaDisconnected"); return ESP_OK; } // apStaDisconnected +/** + * @brief Handle a Scan for APs done event. + * Handle having an ESP32 station scan (APs) done. + * @param [in] event_scan_done system_event_sta_scan_done_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staScanDone(system_event_sta_scan_done_t info) { + ESP_LOGD(LOG_TAG, "default staScanDone"); + return ESP_OK; +} // staScanDone + + +/** + * @brief Handle the auth mode of APs change event. + * Handle having the auth mode of AP ESP32 station connected to changed. + * @param [in] event_auth_change system_event_sta_authmode_change_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staAuthChange(system_event_sta_authmode_change_t info) { + ESP_LOGD(LOG_TAG, "default staAuthChange"); + return ESP_OK; +} // staAuthChange + + WiFiEventHandler::~WiFiEventHandler() { } // ~WiFiEventHandler diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index 612dc8f3..4dd46dba 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -22,13 +22,15 @@ * wifi.startAP("MYSSID", "password"); * * The overridable functions are: - * * esp_err_t apStaConnected() - * * esp_err_t apStaDisconnected() + * * esp_err_t apStaConnected(system_event_ap_staconnected_t info) + * * esp_err_t apStaDisconnected(system_event_ap_stadisconnected_t info) * * esp_err_t apStart() * * esp_err_t apStop() - * * esp_err_t staConnected() - * * esp_err_t staDisconnected() - * * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) + * * esp_err_t staConnected(system_event_sta_connected_t info) + * * esp_err_t staDisconnected(system_event_sta_disconnected_t info) + * * esp_err_t staGotIp(system_event_sta_got_ip_t info) + * * esp_err_t staScanDone(system_event_sta_scan_done_t info) + * * esp_err_t staAuthChange(system_event_sta_authmode_change_t info) * * esp_err_t staStart() * * esp_err_t staStop() * * esp_err_t wifiReady() @@ -56,11 +58,19 @@ * return ESP_OK; * } * - * esp_err_t MyHandler::staConnected() { + * esp_err_t MyHandler::staConnected(system_event_sta_connected_t info) { * return ESP_OK; * } * - * esp_err_t MyHandler::staDisconnected() { + * esp_err_t MyHandler::staDisconnected(system_event_sta_disconnected_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::apStaConnected(system_event_ap_staconnected_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::apStaDisconnected(system_event_ap_stadisconnected_t info) { * return ESP_OK; * } * @@ -68,7 +78,15 @@ * return ESP_OK; * } * - * esp_err_t MyHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { + * esp_err_t MyHandler::staGotIp(system_event_sta_got_ip_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::staScanDone(system_event_sta_scan_done_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::staAuthChange(system_event_sta_authmode_change_t info) { * return ESP_OK; * } * @@ -81,14 +99,16 @@ class WiFiEventHandler { public: WiFiEventHandler(); virtual ~WiFiEventHandler(); - virtual esp_err_t apStaConnected(); - virtual esp_err_t apStaDisconnected(); + virtual esp_err_t apStaConnected(system_event_ap_staconnected_t info); + virtual esp_err_t apStaDisconnected(system_event_ap_stadisconnected_t info); virtual esp_err_t apStart(); virtual esp_err_t apStop(); system_event_cb_t getEventHandler(); - virtual esp_err_t staConnected(); - virtual esp_err_t staDisconnected(); - virtual esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip); + virtual esp_err_t staConnected(system_event_sta_connected_t info); + virtual esp_err_t staDisconnected(system_event_sta_disconnected_t info); + virtual esp_err_t staGotIp(system_event_sta_got_ip_t info); + virtual esp_err_t staScanDone(system_event_sta_scan_done_t info); + virtual esp_err_t staAuthChange(system_event_sta_authmode_change_t info); virtual esp_err_t staStart(); virtual esp_err_t staStop(); virtual esp_err_t wifiReady(); diff --git a/cpp_utils/cpp_utils b/cpp_utils/cpp_utils deleted file mode 120000 index 5d96b78d..00000000 --- a/cpp_utils/cpp_utils +++ /dev/null @@ -1 +0,0 @@ -/home/kolban/esp32/esptest/apps/workspace/esp32-snippets/cpp_utils \ No newline at end of file From cfaa5c7f86d3e69d5de69f11265eb6784fc57d16 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 15 Dec 2017 22:31:00 -0600 Subject: [PATCH 114/310] Changes for #196 --- cpp_utils/BLEAdvertising.cpp | 34 ++++++++++++++++++++++++++++++++++ cpp_utils/BLEAdvertising.h | 27 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 7406819b..e8d9bf4a 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -381,5 +381,39 @@ std::string BLEAdvertisementData::getPayload() { } // getPayload +BLEBeacon::BLEBeacon() { + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // BLEBeacon + +std::string BLEBeacon::getData() { + return std::string((char*)&m_beaconData, sizeof(m_beaconData)); +} // getData + +void BLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = major; +} // setMajor + +void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = manufacturerId; +} // setManufacturerId + +void BLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = minor; +} // setMinior + +void BLEBeacon::setProximityUUID(BLEUUID uuid) { + uuid = uuid.to128(); + memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); +} // setProximityUUID + +void BLEBeacon::setSignalPower(uint16_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 433de603..1965227d 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -13,6 +13,33 @@ #include "BLEUUID.h" #include +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class BLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + uint8_t signalPower; + } m_beaconData; +public: + BLEBeacon(); + void setManufacturerId(uint16_t manufacturerId); + //void setSubType(uint8_t subType); + void setProximityUUID(BLEUUID uuid); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setSignalPower(uint16_t signalPower); + std::string getData(); +}; // BLEBeacon + /** * @brief Advertisement data set by the programmer to be published by the %BLE server. From e2257d9c9ff0b37ccb84e4ff6455e34fbd62748e Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 17 Dec 2017 14:14:27 -0600 Subject: [PATCH 115/310] Fixes for #64 --- cpp_utils/BLEAdvertising.cpp | 11 ++++++----- cpp_utils/GeneralUtils.cpp | 20 ++++++++++++++++++++ cpp_utils/GeneralUtils.h | 2 ++ cpp_utils/HttpRequest.cpp | 16 +++++++++++++++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index e8d9bf4a..6331fa10 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -382,11 +382,12 @@ std::string BLEAdvertisementData::getPayload() { BLEBeacon::BLEBeacon() { - m_beaconData.subType = 0x02; - m_beaconData.subTypeLength = 0x15; - m_beaconData.major = 0; - m_beaconData.minor = 0; - m_beaconData.signalPower = 0; + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); } // BLEBeacon diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 42ea682f..ccf74f79 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -339,6 +339,24 @@ std::string GeneralUtils::ipToString(uint8_t *ip) { } // ipToString +/** + * @brief Split a string into parts based on a delimiter. + * @param [in] source The source string to split. + * @param [in] delimiter The delimiter characters. + * @return A vector of strings that are the split of the input. + */ +std::vector GeneralUtils::split(std::string source, char delimiter) { + // See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g + std::vector strings; + std::istringstream iss(source); + std::string s; + while(std::getline(iss, s, delimiter)) { + strings.push_back(trim(s)); + } + return strings; +} // split + + /** * @brief Convert an ESP error code to a string. * @param [in] errCode The errCode to be converted. @@ -443,3 +461,5 @@ std::string GeneralUtils::trim(const std::string& str) size_t last = str.find_last_not_of(' '); return str.substr(first, (last - first + 1)); } // trim + + diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 9f945071..013953dc 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -11,6 +11,7 @@ #include #include #include +#include /** * @brief General utilities. @@ -24,6 +25,7 @@ class GeneralUtils { static const char* errorToString(esp_err_t errCode); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); + static std::vector split(std::string source, char delimiter); static std::string toLower(std::string& value); static std::string trim(const std::string& str); diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index 5347f195..bbbdce6d 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -33,6 +33,7 @@ #include #include +#include #include "HttpResponse.h" #include "HttpRequest.h" #include "GeneralUtils.h" @@ -96,11 +97,24 @@ HttpRequest::HttpRequest(Socket clientSocket) { m_parser.parse(clientSocket); // Parse the socket stream to build the HTTP data. + // We have to take some special action on the Connection header. We want to know if it contains "Upgrade" + // however it has come to light that the Connection header can contain multiple parts. For example, it has + // been reported that it can contain "keep-alive,Upgrade". Because of this we can't simply examine the string + // to see if it equals "Upgrade". Our solution is to get the value of Connection string, split it by "," as + // a delimiter and then examine each of the parts to see if any of those are "Upgrade". + std::vector parts = GeneralUtils::split(getHeader(HTTP_HEADER_CONNECTION), ','); + bool upgradeFound = false; + if (std::find(parts.begin(), parts.end(), "Upgrade") != parts.end()) + { + upgradeFound = true; + } + // Is this a Web Socket? if (getMethod() == HTTP_METHOD_GET && !getHeader(HTTP_HEADER_HOST).empty() && getHeader(HTTP_HEADER_UPGRADE) == "websocket" && - getHeader(HTTP_HEADER_CONNECTION) == "Upgrade" && + //getHeader(HTTP_HEADER_CONNECTION) == "Upgrade" && + upgradeFound == true && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_KEY).empty() && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_VERSION).empty()) { ESP_LOGD(LOG_TAG, "Websocket detected!"); From 3d39b222179d8ea2f8e981a90eb998da53a12320 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 17 Dec 2017 23:13:13 -0600 Subject: [PATCH 116/310] Code changes for #291 --- cpp_utils/BLEAdvertising.cpp | 31 +++++++++++++++++++++++++++++++ cpp_utils/BLEAdvertising.h | 1 + cpp_utils/BLEDevice.cpp | 28 ++++++++++++++++++++++++++++ cpp_utils/BLEDevice.h | 2 ++ 4 files changed, 62 insertions(+) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 6331fa10..d7b20bb6 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -92,6 +92,36 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { } // setAppearance +/** + * @brief Set the filtering for the scan filter. + * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. + * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. + */ +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); + if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (!scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } +} // setScanFilter + + /** * @brief Set the advertisement data that is to be published in a regular advertisement. * @param [in] advertisementData The data to be advertised. @@ -417,4 +447,5 @@ void BLEBeacon::setSignalPower(uint16_t signalPower) { } // setSignalPower + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 1965227d..04f5924d 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -80,6 +80,7 @@ class BLEAdvertising { void stop(); void setAppearance(uint16_t appearance); void setAdvertisementData(BLEAdvertisementData& advertisementData); + void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); void setScanResponseData(BLEAdvertisementData& advertisementData); private: diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index b89711a1..5e09069d 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -337,4 +337,32 @@ bool initialized = false; // Have we been initialized? return oss.str(); } // toString + +/** + * @brief Add an entry to the BLE white list. + * @param [in] address The address to add to the white list. + */ +void BLEDevice::whiteListAdd(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListAdd"); +} // whiteListAdd + + +/** + * @brief Remove an entry from the BLE white list. + * @param [in] address The address to remove from the white list. + */ +void BLEDevice::whiteListRemove(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListRemove"); +} // whiteListRemove + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index ecf5e6c7..c6383720 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -36,6 +36,8 @@ class BLEDevice { static void setPower(esp_power_level_t powerLevel); // Set our power level. static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server. static std::string toString(); // Return a string representation of our device. + static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list. + static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list. private: static BLEServer *m_pServer; From dcfdd26ba998f52cf08ac69b595773c0aa117bd8 Mon Sep 17 00:00:00 2001 From: chegewara Date: Mon, 18 Dec 2017 20:04:50 +0100 Subject: [PATCH 117/310] Introduced BLE security --- cpp_utils/BLECharacteristic.cpp | 27 ++++-- cpp_utils/BLECharacteristic.h | 4 + cpp_utils/BLEClient.cpp | 50 +++++++++++ cpp_utils/BLEClient.h | 4 + cpp_utils/BLESecurity.cpp | 91 ++++++++++++++++++++ cpp_utils/BLESecurity.h | 60 +++++++++++++ cpp_utils/BLEServer.cpp | 64 +++++++++++++- cpp_utils/BLEServer.h | 8 ++ cpp_utils/SampleSecureClient.cpp | 141 +++++++++++++++++++++++++++++++ cpp_utils/SampleSecureServer.cpp | 88 +++++++++++++++++++ 10 files changed, 529 insertions(+), 8 deletions(-) create mode 100644 cpp_utils/BLESecurity.cpp create mode 100644 cpp_utils/BLESecurity.h create mode 100644 cpp_utils/SampleSecureClient.cpp create mode 100644 cpp_utils/SampleSecureServer.cpp diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 5754c488..9e9aa5c6 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -107,7 +107,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { esp_err_t errRc = ::esp_ble_gatts_add_char( m_pService->getHandle(), getUUID().getNative(), - static_cast(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), + static_cast(m_permissions), getProperties(), //&value, nullptr, @@ -163,6 +163,9 @@ uint16_t BLECharacteristic::getHandle() { return m_handle; } // getHandle +void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) { + m_permissions = perm; +} esp_gatt_char_prop_t BLECharacteristic::getProperties() { return m_properties; @@ -348,12 +351,17 @@ void BLECharacteristic::handleGATTServerEvent( // The following code has deliberately not been factored to make it fewer statements because this would cloud the // the logic flow comprehension. // + uint16_t maxOffset = m_mtu - 1; + if (m_mtu > 512) + maxOffset = 512; + ESP_LOGI(LOG_TAG, "%d", m_mtu); + ESP_LOGI(LOG_TAG, "%d", maxOffset); if (param->read.need_rsp) { ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; std::string value = m_value.getValue(); if (param->read.is_long) { - if (value.length() - m_value.getReadOffset() < 22) { + if (value.length() - m_value.getReadOffset() < maxOffset) { // This is the last in the chain rsp.attr_value.len = value.length() - m_value.getReadOffset(); rsp.attr_value.offset = m_value.getReadOffset(); @@ -361,16 +369,16 @@ void BLECharacteristic::handleGATTServerEvent( m_value.setReadOffset(0); } else { // There will be more to come. - rsp.attr_value.len = 22; + rsp.attr_value.len = maxOffset; rsp.attr_value.offset = m_value.getReadOffset(); memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); - m_value.setReadOffset(rsp.attr_value.offset + 22); + m_value.setReadOffset(rsp.attr_value.offset + maxOffset); } } else { - if (value.length() > 21) { + if (value.length()+1 > maxOffset) { // Too big for a single shot entry. - m_value.setReadOffset(22); - rsp.attr_value.len = 22; + m_value.setReadOffset(maxOffset); + rsp.attr_value.len = maxOffset; rsp.attr_value.offset = 0; memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); } else { @@ -412,6 +420,7 @@ void BLECharacteristic::handleGATTServerEvent( } case ESP_GATTS_CONNECT_EVT: + m_mtu = 23; m_semaphoreConfEvt.give(); break; @@ -419,6 +428,10 @@ void BLECharacteristic::handleGATTServerEvent( m_semaphoreConfEvt.give(); break; + case ESP_GATTS_MTU_EVT : + m_mtu = param->mtu.mtu; + break; + default: { break; } // default diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 7bb54c64..a65bf121 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -13,6 +13,7 @@ #include #include "BLEUUID.h" #include +#include #include "BLEDescriptor.h" #include "BLEValue.h" #include "FreeRTOS.h" @@ -78,6 +79,7 @@ class BLECharacteristic { void setWriteNoResponseProperty(bool value); std::string toString(); uint16_t getHandle(); + void setAccessPermissions(esp_gatt_perm_t perm); static const uint32_t PROPERTY_READ = 1<<0; static const uint32_t PROPERTY_WRITE = 1<<1; @@ -100,6 +102,8 @@ class BLECharacteristic { BLECharacteristicCallbacks* m_pCallbacks; BLEService* m_pService; BLEValue m_value; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + uint16_t m_mtu = 23; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index f3987d08..3db0be64 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -168,6 +168,14 @@ void BLEClient::gattClientEventHandler( break; } // ESP_GATTC_DISCONNECT_EVT + case ESP_GATTS_CONNECT_EVT: { + //m_connId = param->connect.conn_id; // Save the connection id. + if(m_securityLevel){ + esp_ble_set_encryption(evtParam->connect.remote_bda, m_securityLevel); + //memcpy(m_remote_bda, param->connect.remote_bda, sizeof(m_remote_bda)); + } + break; + } // ESP_GATTS_CONNECT_EVT // // ESP_GATTC_OPEN_EVT @@ -411,6 +419,41 @@ void BLEClient::handleGAPEvent( break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); + assert(m_securityCallbacks!=nullptr); + // esp_ble_passkey_reply(m_remote_bda, true, m_securityCallbacks->onPassKeyRequest()); + break; + + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + if(m_securityCallbacks!=nullptr) + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, m_securityCallbacks->onSecurityRequest()); + else + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, false); + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer deivce. + if(m_securityCallbacks!=nullptr) + m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key info share with peer device to the user. + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + if(m_securityCallbacks!=nullptr) + m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + break; + default: break; } @@ -448,6 +491,13 @@ void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::s ESP_LOGD(LOG_TAG, "<< setValue"); } // setValue +void BLEClient::setEncryptionLevel(esp_ble_sec_act_t level) { + m_securityLevel = level; +} + +void BLEClient::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + m_securityCallbacks = callbacks; +} /** * @brief Return a string representation of this client. diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index a60ed102..1a630dfe 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -49,6 +49,8 @@ class BLEClient { void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. + void setEncryptionLevel(esp_ble_sec_act_t level); + void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); std::string toString(); // Return a string representation of this client. @@ -80,6 +82,8 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; void clearServices(); // Clear any existing services. + esp_ble_sec_act_t m_securityLevel = (esp_ble_sec_act_t)0; + BLESecurityCallbacks* m_securityCallbacks = 0; }; // class BLEDevice diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp new file mode 100644 index 00000000..f3e5bbe9 --- /dev/null +++ b/cpp_utils/BLESecurity.cpp @@ -0,0 +1,91 @@ +/* + * BLESecurity.cpp + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#include + +BLESecurity::BLESecurity() { +} + +BLESecurity::~BLESecurity() { +} +/* + * @brief Set requested authentication mode + */ +void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { + m_authReq = auth_req; + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode +} +/* + * @brief Set our device IO capability to let end user perform authorization + * either by displaying or entering generated 6-digits pin code + */ +void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { + m_iocap = iocap; + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); +} +/* + * @brief Init encryption key by server + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setInitEncryptionKey(uint8_t init_key, uint8_t key_size) { + m_initKey = init_key; + m_keySize = key_size; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); // <--- setup encryption key max size +} + +/* + * @brief Init encryption key by client + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setRespEncryptionKey(uint8_t resp_key, uint8_t key_size) { + m_respKey = resp_key; + m_keySize = key_size; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); // <--- setup encryption key max size +} +/* + * @brief Debug function to display what keys are exchanged by peers + */ +char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) +{ + char *key_str = NULL; + switch(key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*)"ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*)"ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*)"ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*)"ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*)"ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*)"ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = (char*)"ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = (char*)"ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = (char*)"ESP_LE_KEY_LCSRK"; + break; + default: + key_str = (char*)"INVALID BLE KEY TYPE"; + break; + + } + return key_str; +} diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h new file mode 100644 index 00000000..494a1b7c --- /dev/null +++ b/cpp_utils/BLESecurity.h @@ -0,0 +1,60 @@ +/* + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include + +class BLESecurity { +public: + BLESecurity(); + virtual ~BLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key, uint8_t key_size = 16); + void setRespEncryptionKey(uint8_t resp_key, uint8_t key_size = 16); + static char* esp_key_type_to_str(esp_ble_key_type_t key_type); + +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; +}; + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class BLESecurityCallbacks { +public: + virtual ~BLESecurityCallbacks() {}; + + /* + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + /* + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key); + /* + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + virtual bool onSecurityRequest(); + /* + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t); +}; + +#endif /* COMPONENTS_CPP_UTILS_BLESECURITY_H_ */ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 75805135..a3ea7681 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +//#include #include "BLEDevice.h" #include "BLEServer.h" #include "BLEService.h" @@ -151,6 +151,53 @@ void BLEServer::handleGAPEvent( */ break; } + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); + assert(m_securityCallbacks!=nullptr); + esp_ble_passkey_reply(m_remote_bda, true, m_securityCallbacks->onPassKeyRequest()); + break; + + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + if(m_securityCallbacks!=nullptr) + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, m_securityCallbacks->onSecurityRequest()); + else + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, false); + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer deivce. + if(m_securityCallbacks!=nullptr) + m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key info share with peer device to the user. + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + if(m_securityCallbacks!=nullptr) + m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + break; + default: break; } @@ -198,6 +245,10 @@ void BLEServer::handleGATTServerEvent( // case ESP_GATTS_CONNECT_EVT: { m_connId = param->connect.conn_id; // Save the connection id. + if(m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, m_securityLevel); + memcpy(m_remote_bda, param->connect.remote_bda, sizeof(m_remote_bda)); + } if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); } @@ -334,6 +385,17 @@ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising +void BLEServer::setEncryptionLevel(esp_ble_sec_act_t level) { + m_securityLevel = level; +} + +uint32_t BLEServer::getPassKey() { + return m_securityPassKey; +} + +void BLEServer::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + m_securityCallbacks = callbacks; +} void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 524e88f3..8d4554d2 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -18,6 +18,7 @@ #include "BLEAdvertising.h" #include "BLECharacteristic.h" #include "BLEService.h" +#include "BLESecurity.h" #include "FreeRTOS.h" class BLEServerCallbacks; @@ -57,6 +58,9 @@ class BLEServer { BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); + void setEncryptionLevel(esp_ble_sec_act_t level); + uint32_t getPassKey(); + void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); private: @@ -74,6 +78,10 @@ class BLEServer { FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); BLEServiceMap m_serviceMap; BLEServerCallbacks* m_pServerCallbacks; + esp_ble_sec_act_t m_securityLevel = (esp_ble_sec_act_t)0; + esp_bd_addr_t m_remote_bda; + uint32_t m_securityPassKey; + BLESecurityCallbacks* m_securityCallbacks; void createApp(uint16_t appId); uint16_t getConnId(); diff --git a/cpp_utils/SampleSecureClient.cpp b/cpp_utils/SampleSecureClient.cpp new file mode 100644 index 00000000..457988ed --- /dev/null +++ b/cpp_utils/SampleSecureClient.cpp @@ -0,0 +1,141 @@ +/** + * Create a sample BLE client that connects to a BLE server and then retrieves the current + * characteristic value. It will then periodically update the value of the characteristic on the + * remote server with the current time since boot. + */ +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onSecurityRequest(){ + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + pClient->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + pClient->setSecurityCallbacks(new MySecurity()); + + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleSecureClient(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(15); +} // SampleClient diff --git a/cpp_utils/SampleSecureServer.cpp b/cpp_utils/SampleSecureServer.cpp new file mode 100644 index 00000000..97c9037f --- /dev/null +++ b/cpp_utils/SampleSecureServer.cpp @@ -0,0 +1,88 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onSecurityRequest(){ + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + pServer->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + pServer->setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + pSecurity->setCapability(ESP_IO_CAP_NONE); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleSecureServer(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main From d05c381022300f497df0d66cb6f8f9b1f72dbf9a Mon Sep 17 00:00:00 2001 From: chegewara Date: Mon, 18 Dec 2017 22:54:47 +0100 Subject: [PATCH 118/310] Introduce BLE HID class --- cpp_utils/BLEHIDDevice.cpp | 154 +++++++ cpp_utils/BLEHIDDevice.h | 87 ++++ cpp_utils/HIDTypes.h | 91 ++++ cpp_utils/tests/BLETests/SampleHIDDevice.cpp | 220 ++++++++++ .../tests/BLETests/SampleKeyboardTypes.h | 402 ++++++++++++++++++ .../BLETests}/SampleSecureClient.cpp | 2 +- .../BLETests}/SampleSecureServer.cpp | 0 7 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 cpp_utils/BLEHIDDevice.cpp create mode 100644 cpp_utils/BLEHIDDevice.h create mode 100644 cpp_utils/HIDTypes.h create mode 100644 cpp_utils/tests/BLETests/SampleHIDDevice.cpp create mode 100644 cpp_utils/tests/BLETests/SampleKeyboardTypes.h rename cpp_utils/{ => tests/BLETests}/SampleSecureClient.cpp (98%) rename cpp_utils/{ => tests/BLETests}/SampleSecureServer.cpp (100%) diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp new file mode 100644 index 00000000..c5485a81 --- /dev/null +++ b/cpp_utils/BLEHIDDevice.cpp @@ -0,0 +1,154 @@ +/* + * BLEHIDDevice.cpp + * + * Created on: Dec 18, 2017 + * Author: chegewara + */ +//#include "BLEUUID.h" +#include "BLEHIDDevice.h" + +BLEHIDDevice::BLEHIDDevice(BLEServer* server) { + m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a)); + m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40); + //m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); + createDescriptors(); + createCharacteristics(); +} + +BLEHIDDevice::~BLEHIDDevice() { + // TODO Auto-generated destructor stub +} + +void BLEHIDDevice::setReportMap(uint8_t* map, uint16_t size) { + m_reportMapCharacteristic->setValue(map, size); +} + +void BLEHIDDevice::createDescriptors() { + m_inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + const uint8_t desc1_val[] = {0x01}; + m_inputReportDescriptor->setValue((uint8_t*)desc1_val, 1); + m_inputReportNotifications = new BLE2902(); + + m_outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + const uint8_t desc2_val[] = {0x02}; + m_outputReportDescriptor->setValue((uint8_t*)desc2_val, 1); + + m_featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + const uint8_t desc3_val[] = {0x03}; + m_featureReportDescriptor->setValue((uint8_t*)desc3_val, 1); + + m_bootInputNotifications = new BLE2902(); + + if(m_batteryService != nullptr){ + m_batteryLevelDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2904)); + m_batteryLevelNotifications = new BLE2902(); + } +} + +void BLEHIDDevice::createCharacteristics() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, BLECharacteristic::PROPERTY_READ); + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, BLECharacteristic::PROPERTY_READ); + + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, BLECharacteristic::PROPERTY_READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, BLECharacteristic::PROPERTY_READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); + m_inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + m_outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); + m_featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR); + m_bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY); + m_bootOutputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + + m_inputReportCharacteristic->addDescriptor(m_inputReportDescriptor); + m_inputReportCharacteristic->addDescriptor(m_inputReportNotifications); + m_outputReportCharacteristic->addDescriptor(m_outputReportDescriptor); + m_featureReportCharacteristic->addDescriptor(m_featureReportDescriptor); + m_bootInputCharacteristic->addDescriptor(m_bootInputNotifications); + if(m_batteryService != nullptr){ + m_batteryLevelCharacteristic->addDescriptor(m_batteryLevelDescriptor); //OPTIONAL? + m_batteryLevelCharacteristic->addDescriptor(m_batteryLevelNotifications); //OPTIONAL? + } +} + +void BLEHIDDevice::startServices() { + m_deviceInfoService->start(); + m_hidService->start(); + if(m_batteryService!=nullptr) + m_batteryService->start(); +} + +BLEService* BLEHIDDevice::deviceInfo() { + return m_deviceInfoService; +} + +BLEService* BLEHIDDevice::hidService() { + return m_hidService; +} + +BLEService* BLEHIDDevice::batteryService() { + return m_batteryService; +} + +BLECharacteristic* BLEHIDDevice::manufacturer() { + return m_manufacturerCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::pnp() { + return m_pnpCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::hidInfo() { + return m_hidInfoCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::reportMap() { + return m_reportMapCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::hidControl() { + return m_hidControlCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::inputReport(void*) { + return m_inputReportCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::outputReport(void*) { + return m_outputReportCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::featureReport(void*) { + return m_featureReportCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::protocolMode() { + return m_protocolModeCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::bootInput() { + return m_bootInputCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::bootOutput() { + return m_bootOutputCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::batteryLevel(void*) { + return m_batteryLevelCharacteristic; +} + +BLEDescriptor* BLEHIDDevice::inputReport() { + return m_inputReportDescriptor; +} + +BLEDescriptor* BLEHIDDevice::outputReport() { + return m_outputReportDescriptor; +} + +BLEDescriptor* BLEHIDDevice::featureReport() { + return m_featureReportDescriptor; +} + +BLEDescriptor* BLEHIDDevice::batteryLevel() { + return m_batteryLevelDescriptor; +} diff --git a/cpp_utils/BLEHIDDevice.h b/cpp_utils/BLEHIDDevice.h new file mode 100644 index 00000000..43e4ccdc --- /dev/null +++ b/cpp_utils/BLEHIDDevice.h @@ -0,0 +1,87 @@ +/* + * BLEHIDDevice.h + * + * Created on: Dec 18, 2017 + * Author: chegewara + */ + +#ifndef _BLEHIDDEVICE_H_ +#define _BLEHIDDEVICE_H_ +#include "BLECharacteristic.h" +#include "BLEService.h" +#include "BLEDescriptor.h" +#include "BLE2902.h" +#include "HIDTypes.h" + +#define GENERIC_HID 960 +#define HID_KEYBOARD 961 +#define HID_MOUSE 962 +#define HID_JOYSTICK 963 +#define HID_GAMEPAD 964 +#define HID_TABLET 965 +#define HID_CARD_READER 966 +#define HID_DIGITAL_PEN 967 +#define HID_BARCODE 968 + +class BLEHIDDevice { +public: + BLEHIDDevice(BLEServer*); + virtual ~BLEHIDDevice(); + + void setReportMap(uint8_t* map, uint16_t); + void startServices(); + + BLEService* deviceInfo(); + BLEService* hidService(); + BLEService* batteryService(); + + BLECharacteristic* manufacturer(); + BLECharacteristic* pnp(); + BLECharacteristic* hidInfo(); + BLECharacteristic* reportMap(); + BLECharacteristic* hidControl(); + BLECharacteristic* inputReport(void*); + BLECharacteristic* outputReport(void*); + BLECharacteristic* featureReport(void*); + BLECharacteristic* protocolMode(); + BLECharacteristic* bootInput(); + BLECharacteristic* bootOutput(); + BLECharacteristic* batteryLevel(void*); + + BLEDescriptor* inputReport(); + BLEDescriptor* outputReport(); + BLEDescriptor* featureReport(); + BLEDescriptor* batteryLevel(); + +private: + void createCharacteristics(); + void createDescriptors(); + + BLEService* m_deviceInfoService; //0x180a + BLEService* m_hidService; //0x1812 + BLEService* m_batteryService = 0; //0x180f + + BLECharacteristic* m_manufacturerCharacteristic; //0x2a29 + BLECharacteristic* m_pnpCharacteristic; //0x2a50 + BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a + BLECharacteristic* m_reportMapCharacteristic; //0x2a4b + BLECharacteristic* m_hidControlCharacteristic; //0x2a4c + BLECharacteristic* m_inputReportCharacteristic; //0x2a4d + BLECharacteristic* m_outputReportCharacteristic; //0x2a4d + BLECharacteristic* m_featureReportCharacteristic; //0x2a4d + BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e + BLECharacteristic* m_bootInputCharacteristic; //0x2a22 + BLECharacteristic* m_bootOutputCharacteristic; //0x2a32 + BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 + + BLEDescriptor* m_inputReportDescriptor; //0x2908 + BLEDescriptor* m_outputReportDescriptor; //0x2908 + BLEDescriptor* m_featureReportDescriptor; //0x2908 + BLE2902* m_inputReportNotifications; //0x2902 + BLE2902* m_bootInputNotifications; //0x2902 + BLEDescriptor* m_batteryLevelDescriptor; //0x2904 + BLE2902* m_batteryLevelNotifications; //0x2902 + +}; + +#endif /* _BLEHIDDEVICE_H_ */ diff --git a/cpp_utils/HIDTypes.h b/cpp_utils/HIDTypes.h new file mode 100644 index 00000000..b8b181be --- /dev/null +++ b/cpp_utils/HIDTypes.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/cpp_utils/tests/BLETests/SampleHIDDevice.cpp b/cpp_utils/tests/BLETests/SampleHIDDevice.cpp new file mode 100644 index 00000000..c3a4df1a --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleHIDDevice.cpp @@ -0,0 +1,220 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#include "SampleKeyboardTypes.h" +#include +#include +#include + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleHIDDevice"; + +static BLEHIDDevice* hid; + +class MyTask : public Task { + void run(void*){ + vTaskDelay(10000); + const char* hello = "Hello world from esp32 hid keyboard!!!"; + while(*hello){ + KEYMAP map = keymap[(uint8_t)*hello]; + uint8_t a[] = {map.modifier, 0x0, map.usage, 0x0,0x0,0x0,0x0,0x0}; + hid->inputReport(NULL)->setValue(a,sizeof(a)); + hid->inputReport(NULL)->notify(); + + hello++; + } + uint8_t v[] = {0x0, 0x0, 0x0, 0x0,0x0,0x0,0x0,0x0}; + hid->inputReport(NULL)->setValue(v, sizeof(v)); + hid->inputReport(NULL)->notify(); + vTaskDelete(NULL); + } +}; + MyTask *task; + + class MyCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer){ + task->start(); + } + + void onDisconnect(BLEServer* pServer){ + + } + }; +uint32_t passKey = 0; + class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGE(LOG_TAG, "The passkey request %d", passKey); + + vTaskDelay(25000); + return passKey; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + passKey = pass_key; + } + bool onSecurityRequest(){ + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } + }; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + task = new MyTask(); + BLEDevice::init("ESP32"); + BLEServer *pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyCallbacks()); + pServer->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + pServer->setSecurityCallbacks(new MySecurity()); + + /* + * Instantiate hid device + */ + hid = new BLEHIDDevice(pServer); + + /* + * Set manufacturer name + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.manufacturer_name_string.xml + */ + std::string name = "esp-community"; + hid->manufacturer()->setValue(name); + + /* + * Set pnp parameters + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.pnp_id.xml + */ + const uint8_t pnp[] = {0x01,0x02,0xe5,0xab,0xcd,0x01,0x10}; + hid->pnp()->setValue((uint8_t*)pnp, sizeof(pnp)); + + /* + * Set hid informations + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml + */ + const uint8_t val1[] = {0x01,0x11,0x00,0x03}; + hid->hidInfo()->setValue((uint8_t*)val1, 4); + + /* + * Mouse + */ + const uint8_t reportMap2[] = { + USAGE_PAGE(1), 0x01, + USAGE(1), 0x02, + COLLECTION(1), 0x01, + USAGE(1), 0x01, + COLLECTION(1), 0x00, + USAGE_PAGE(1), 0x09, + USAGE_MINIMUM(1), 0x1, + USAGE_MAXIMUM(1), 0x3, + LOGICAL_MINIMUM(1), 0x0, + LOGICAL_MAXIMUM(1), 0x1, + REPORT_COUNT(1), 0x3, + REPORT_SIZE(1), 0x1, + INPUT(1), 0x2, // (Data, Variable, Absolute), ;3 button bits + REPORT_COUNT(1), 0x1, + REPORT_SIZE(1), 0x5, + INPUT(1), 0x1, //(Constant), ;5 bit padding + USAGE_PAGE(1), 0x1, //(Generic Desktop), + USAGE(1), 0x30, + USAGE(1), 0x31, + LOGICAL_MINIMUM(1), 0x81, + LOGICAL_MAXIMUM(1), 0x7f, + REPORT_SIZE(1), 0x8, + REPORT_COUNT(1), 0x2, + INPUT(1), 0x6, //(Data, Variable, Relative), ;2 position bytes (X & Y) + END_COLLECTION(0), + END_COLLECTION(0) + }; + /* + * Keyboard + */ + const uint8_t reportMap[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, // 1 byte (Modifier) + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x01, // 1 byte (Reserved) + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, // Num Lock + USAGE_MAXIMUM(1), 0x05, // Kana + OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x01, // 3 bits (Padding) + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x06, // 6 bytes (Keys) + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, // 101 keys + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + END_COLLECTION(0) + }; + /* + * Set report map (here is initialized device driver on client side) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.report_map.xml + */ + hid->setReportMap((uint8_t*)reportMap, sizeof(reportMap)); + + /* + * We are prepared to start hid device services. Before this point we can change all values and/or set parameters we need. + * Also before we start, if we want to provide battery info, we need to prepare battery service. + * We can setup characteristics authorization + */ + hid->startServices(); + + /* + * Its good to setup advertising by providing appearance and advertised service. This will let clients find our device by type + */ + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->setAppearance(HID_KEYBOARD); + pAdvertising->addServiceUUID(hid->hidService()->getUUID()); + pAdvertising->start(); + + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + pSecurity->setCapability(ESP_IO_CAP_NONE); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleHID(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main diff --git a/cpp_utils/tests/BLETests/SampleKeyboardTypes.h b/cpp_utils/tests/BLETests/SampleKeyboardTypes.h new file mode 100644 index 00000000..ef48a526 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/cpp_utils/SampleSecureClient.cpp b/cpp_utils/tests/BLETests/SampleSecureClient.cpp similarity index 98% rename from cpp_utils/SampleSecureClient.cpp rename to cpp_utils/tests/BLETests/SampleSecureClient.cpp index 457988ed..859291cb 100644 --- a/cpp_utils/SampleSecureClient.cpp +++ b/cpp_utils/tests/BLETests/SampleSecureClient.cpp @@ -56,7 +56,7 @@ class MyClient: public Task { void run(void* data) { BLESecurity *pSecurity = new BLESecurity(); pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM); - pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setCapability(ESP_IO_CAP_KBDISP); pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); BLEAddress* pAddress = (BLEAddress*)data; diff --git a/cpp_utils/SampleSecureServer.cpp b/cpp_utils/tests/BLETests/SampleSecureServer.cpp similarity index 100% rename from cpp_utils/SampleSecureServer.cpp rename to cpp_utils/tests/BLETests/SampleSecureServer.cpp From c5ac7f751b30d506fc5eb4d4019a28958093dcd6 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 19 Dec 2017 05:03:54 +0100 Subject: [PATCH 119/310] IBeacon fix --- cpp_utils/BLEAdvertising.cpp | 8 ++++---- cpp_utils/BLEAdvertising.h | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index d7b20bb6..e026cbec 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -426,15 +426,15 @@ std::string BLEBeacon::getData() { } // getData void BLEBeacon::setMajor(uint16_t major) { - m_beaconData.major = major; + m_beaconData.major = ENDIAN_CHANGE_U16(major); } // setMajor void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { - m_beaconData.manufacturerId = manufacturerId; + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); } // setManufacturerId void BLEBeacon::setMinor(uint16_t minor) { - m_beaconData.minor = minor; + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); } // setMinior void BLEBeacon::setProximityUUID(BLEUUID uuid) { @@ -442,7 +442,7 @@ void BLEBeacon::setProximityUUID(BLEUUID uuid) { memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); } // setProximityUUID -void BLEBeacon::setSignalPower(uint16_t signalPower) { +void BLEBeacon::setSignalPower(int8_t signalPower) { m_beaconData.signalPower = signalPower; } // setSignalPower diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 04f5924d..8d53b64e 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -12,6 +12,7 @@ #include #include "BLEUUID.h" #include +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) /** * @brief Representation of a beacon. @@ -27,8 +28,8 @@ class BLEBeacon { uint8_t proximityUUID[16]; uint16_t major; uint16_t minor; - uint8_t signalPower; - } m_beaconData; + int8_t signalPower; + } __attribute__((packed))m_beaconData; public: BLEBeacon(); void setManufacturerId(uint16_t manufacturerId); @@ -36,7 +37,7 @@ class BLEBeacon { void setProximityUUID(BLEUUID uuid); void setMajor(uint16_t major); void setMinor(uint16_t minor); - void setSignalPower(uint16_t signalPower); + void setSignalPower(int8_t signalPower); std::string getData(); }; // BLEBeacon From 189c42c41d9654ba8dab9e3beb347adf7c7500e6 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 19 Dec 2017 20:16:02 -0600 Subject: [PATCH 120/310] Changes for #298 --- cpp_utils/JSON.cpp | 23 +++++++++++++++++++++++ cpp_utils/JSON.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 5cf1e1ed..1f752c63 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -189,6 +189,18 @@ std::string JsonArray::toString() { } // toString +/** + * @brief Build an unformatted string representation. + * @return A string representation. + */ +std::string JsonArray::toStringUnformatted() { + char *data = cJSON_PrintUnformatted(m_node); + std::string ret(data); + free(data); + return ret; +} // toStringUnformatted + + /** * @brief Get the number of elements from the array. * @return The int value that represents the number of elements. @@ -363,3 +375,14 @@ std::string JsonObject::toString() { return ret; } // toString + +/** + * @brief Build an unformatted string representation. + * @return A string representation. + */ +std::string JsonObject::toStringUnformatted() { + char *data = cJSON_PrintUnformatted(m_node); + std::string ret(data); + free(data); + return ret; +} // toStringUnformatted diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index 6f4be9b9..13f8f0a2 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -45,6 +45,7 @@ class JsonArray { void addObject(JsonObject value); void addString(std::string value); std::string toString(); + std::string toStringUnformatted(); std::size_t size(); private: JsonArray(cJSON* node); @@ -77,6 +78,7 @@ class JsonObject { void setObject(std::string name, JsonObject value); void setString(std::string name, std::string value); std::string toString(); + std::string toStringUnformatted(); private: JsonObject(cJSON* node); From 756f2dcfa14398e222b73125b4401b93bcf4305c Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 21 Dec 2017 10:59:02 +0100 Subject: [PATCH 121/310] Add ServiceData GAP event --- cpp_utils/BLEAdvertisedDevice.cpp | 20 ++++++++++++++++++++ cpp_utils/BLEAdvertisedDevice.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 4347526f..c948e552 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -132,6 +132,14 @@ int8_t BLEAdvertisedDevice::getTXPower() { return m_txPower; } // getTXPower +/** + * @brief Get the service data. + * @return The ServiceData of the advertised device. + */ +uint8_t* BLEAdvertisedDevice::getServiceData() { + return m_serviceData; +} //getServiceData + /** * @brief Does this advertisement have an appearance value? * @return True if there is an appearance value present. @@ -274,6 +282,11 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE + case ESP_BLE_AD_TYPE_SERVICE_DATA: { + setServiceData(payload); + break; + } //ESP_BLE_AD_TYPE_SERVICE_DATA + default: { ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type); break; @@ -393,6 +406,13 @@ void BLEAdvertisedDevice::setTXPower(int8_t txPower) { ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower); } // setTXPower +/** + * @brief Set the ServiceData value. + * @param [in] data ServiceData value. + */ +void BLEAdvertisedDevice::setServiceData(uint8_t* data) { + m_serviceData = data; +} //setServiceData /** * @brief Create a string representation of this device. diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index aea6da08..25cc54e1 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -37,6 +37,7 @@ class BLEAdvertisedDevice { BLEScan* getScan(); BLEUUID getServiceUUID(); int8_t getTXPower(); + uint8_t* getServiceData(); bool isAdvertisingService(BLEUUID uuid); bool haveAppearance(); @@ -63,6 +64,7 @@ class BLEAdvertisedDevice { void setServiceUUID(const char* serviceUUID); void setServiceUUID(BLEUUID serviceUUID); void setTXPower(int8_t txPower); + void setServiceData(uint8_t* data); bool m_haveAppearance; bool m_haveManufacturerData; @@ -82,6 +84,7 @@ class BLEAdvertisedDevice { int m_rssi; std::vector m_serviceUUIDs; int8_t m_txPower; + uint8_t* m_serviceData; }; /** From bcc7f6942a06e0a627007e45d23bb7fee1d0fc04 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 21 Dec 2017 15:50:15 -0600 Subject: [PATCH 122/310] Updates for #282 --- cpp_utils/BLEAdvertisedDevice.cpp | 69 +++++++++++++++++++------------ cpp_utils/BLEAdvertisedDevice.h | 11 +++-- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index c948e552..eb546ee9 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -29,6 +29,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() { m_manufacturerData = ""; m_name = ""; m_rssi = -9999; + m_serviceData = ""; m_txPower = 0; m_pScan = nullptr; @@ -36,6 +37,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() { m_haveManufacturerData = false; m_haveName = false; m_haveRSSI = false; + m_haveServiceData = false; m_haveServiceUUID = false; m_haveTXPower = false; @@ -65,7 +67,7 @@ BLEAddress BLEAdvertisedDevice::getAddress() { */ uint16_t BLEAdvertisedDevice::getAppearance() { return m_appearance; -} +} // getAppearance /** @@ -74,7 +76,7 @@ uint16_t BLEAdvertisedDevice::getAppearance() { */ std::string BLEAdvertisedDevice::getManufacturerData() { return m_manufacturerData; -} +} // getManufacturerData /** @@ -104,6 +106,15 @@ BLEScan* BLEAdvertisedDevice::getScan() { } // getScan +/** + * @brief Get the service data. + * @return The ServiceData of the advertised device. + */ +std::string BLEAdvertisedDevice::getServiceData() { + return m_serviceData; +} //getServiceData + + /** * @brief Get the Service UUID. * @return The Service UUID of the advertised device. @@ -132,13 +143,7 @@ int8_t BLEAdvertisedDevice::getTXPower() { return m_txPower; } // getTXPower -/** - * @brief Get the service data. - * @return The ServiceData of the advertised device. - */ -uint8_t* BLEAdvertisedDevice::getServiceData() { - return m_serviceData; -} //getServiceData + /** * @brief Does this advertisement have an appearance value? @@ -176,6 +181,15 @@ bool BLEAdvertisedDevice::haveRSSI() { } // haveRSSI +/** + * @brief Does this advertisement have a service data value? + * @return True if there is a service data value present. + */ +bool BLEAdvertisedDevice::haveServiceData() { + return m_haveServiceData; +} // haveServiceData + + /** * @brief Does this advertisement have a service UUID value? * @return True if there is a service UUID value present. @@ -213,8 +227,8 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { bool finished = false; while(!finished) { - length = *payload; // Retrieve the length of the record. - payload++; // Skip to type + length = *payload; // Retrieve the length of the record. + payload++; // Skip to type sizeConsumed += 1 + length; // increase the size consumed. if (length != 0) { // A length of 0 indicates that we have reached the end. @@ -227,15 +241,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { ad_type, BLEUtils::advTypeToString(ad_type), length, pHex); free(pHex); - - switch(ad_type) { - case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09 + case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09 setName(std::string(reinterpret_cast(payload), length)); break; } // ESP_BLE_AD_TYPE_NAME_CMPL - case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A + case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A setTXPower(*payload); break; } // ESP_BLE_AD_TYPE_TX_PWR @@ -245,13 +257,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } // ESP_BLE_AD_TYPE_APPEARANCE - case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01 + case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01 setAdFlag(*payload); break; } // ESP_BLE_AD_TYPE_FLAG case ESP_BLE_AD_TYPE_16SRV_CMPL: - case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 + case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 for (int var = 0; var < length/2; ++var) { setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*2))); } @@ -259,7 +271,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { } // ESP_BLE_AD_TYPE_16SRV_PART case ESP_BLE_AD_TYPE_32SRV_CMPL: - case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 + case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 for (int var = 0; var < length/4; ++var) { setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4))); } @@ -282,8 +294,8 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE - case ESP_BLE_AD_TYPE_SERVICE_DATA: { - setServiceData(payload); + case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) + setServiceData(std::string(reinterpret_cast(payload), length)); break; } //ESP_BLE_AD_TYPE_SERVICE_DATA @@ -396,6 +408,16 @@ void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) { } // setServiceUUID +/** + * @brief Set the ServiceData value. + * @param [in] data ServiceData value. + */ +void BLEAdvertisedDevice::setServiceData(std::string serviceData) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceData = serviceData; // Save the service data that we received. +} //setServiceData + + /** * @brief Set the power level for this device. * @param [in] txPower The discovered power level. @@ -406,13 +428,6 @@ void BLEAdvertisedDevice::setTXPower(int8_t txPower) { ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower); } // setTXPower -/** - * @brief Set the ServiceData value. - * @param [in] data ServiceData value. - */ -void BLEAdvertisedDevice::setServiceData(uint8_t* data) { - m_serviceData = data; -} //setServiceData /** * @brief Create a string representation of this device. diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index 25cc54e1..14afff90 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -35,15 +35,17 @@ class BLEAdvertisedDevice { std::string getName(); int getRSSI(); BLEScan* getScan(); + std::string getServiceData(); BLEUUID getServiceUUID(); int8_t getTXPower(); - uint8_t* getServiceData(); - bool isAdvertisingService(BLEUUID uuid); + + bool isAdvertisingService(BLEUUID uuid); bool haveAppearance(); bool haveManufacturerData(); bool haveName(); bool haveRSSI(); + bool haveServiceData(); bool haveServiceUUID(); bool haveTXPower(); @@ -64,12 +66,13 @@ class BLEAdvertisedDevice { void setServiceUUID(const char* serviceUUID); void setServiceUUID(BLEUUID serviceUUID); void setTXPower(int8_t txPower); - void setServiceData(uint8_t* data); + void setServiceData(std::string data); bool m_haveAppearance; bool m_haveManufacturerData; bool m_haveName; bool m_haveRSSI; + bool m_haveServiceData; bool m_haveServiceUUID; bool m_haveTXPower; @@ -84,7 +87,7 @@ class BLEAdvertisedDevice { int m_rssi; std::vector m_serviceUUIDs; int8_t m_txPower; - uint8_t* m_serviceData; + std::string m_serviceData; }; /** From 95a20ebae4c8f0b161c36e6be51b57ecfc7ae63b Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 21 Dec 2017 17:57:45 -0600 Subject: [PATCH 123/310] More changes for #282 --- cpp_utils/BLEAdvertisedDevice.cpp | 60 +++++++++++++++++++++++++++++-- cpp_utils/BLEAdvertisedDevice.h | 6 +++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index eb546ee9..351c5e10 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -115,6 +115,15 @@ std::string BLEAdvertisedDevice::getServiceData() { } //getServiceData +/** + * @brief Get the service data UUID. + * @return The service data UUID. + */ +BLEUUID BLEAdvertisedDevice::getServiceDataUUID() { + return m_serviceDataUUID; +} // getServiceDataUUID + + /** * @brief Get the Service UUID. * @return The Service UUID of the advertised device. @@ -294,11 +303,45 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE - case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - setServiceData(std::string(reinterpret_cast(payload), length)); + case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID + if (length < 2) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); + break; + } + uint16_t uuid = *(uint16_t *)payload; + setServiceDataUUID(BLEUUID(uuid)); + if (length > 2) { + setServiceData(std::string(reinterpret_cast(payload+2), length-2)); + } break; } //ESP_BLE_AD_TYPE_SERVICE_DATA + case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID + if (length < 4) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); + break; + } + uint32_t uuid = *(uint32_t *)payload; + setServiceDataUUID(BLEUUID(uuid)); + if (length > 4) { + setServiceData(std::string(reinterpret_cast(payload+4), length-4)); + } + break; + } //ESP_BLE_AD_TYPE_32SERVICE_DATA + + case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID + if (length < 16) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); + break; + } + + setServiceDataUUID(BLEUUID(payload, (size_t)16, false)); + if (length > 16) { + setServiceData(std::string(reinterpret_cast(payload+16), length-16)); + } + break; + } //ESP_BLE_AD_TYPE_32SERVICE_DATA + default: { ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type); break; @@ -418,6 +461,16 @@ void BLEAdvertisedDevice::setServiceData(std::string serviceData) { } //setServiceData +/** + * @brief Set the ServiceDataUUID value. + * @param [in] data ServiceDataUUID value. + */ +void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceDataUUID = uuid; +} // setServiceDataUUID + + /** * @brief Set the power level for this device. * @param [in] txPower The discovered power level. @@ -453,5 +506,8 @@ std::string BLEAdvertisedDevice::toString() { return ss.str(); } // toString + + + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index 14afff90..41bc4c66 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -36,6 +36,7 @@ class BLEAdvertisedDevice { int getRSSI(); BLEScan* getScan(); std::string getServiceData(); + BLEUUID getServiceDataUUID(); BLEUUID getServiceUUID(); int8_t getTXPower(); @@ -63,10 +64,12 @@ class BLEAdvertisedDevice { void setName(std::string name); void setRSSI(int rssi); void setScan(BLEScan* pScan); + void setServiceData(std::string data); + void setServiceDataUUID(BLEUUID uuid); void setServiceUUID(const char* serviceUUID); void setServiceUUID(BLEUUID serviceUUID); void setTXPower(int8_t txPower); - void setServiceData(std::string data); + bool m_haveAppearance; bool m_haveManufacturerData; @@ -88,6 +91,7 @@ class BLEAdvertisedDevice { std::vector m_serviceUUIDs; int8_t m_txPower; std::string m_serviceData; + BLEUUID m_serviceDataUUID; }; /** From ba3d7ac1faa3d23c0fd8426b4a19b37f3bb0831a Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 22 Dec 2017 11:46:32 -0600 Subject: [PATCH 124/310] Fixes for #308 --- cpp_utils/BLEDevice.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 5e09069d..0f50148b 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -48,7 +48,13 @@ bool initialized = false; // Have we been initialized? * @return A new instance of the client. */ /* STATIC */ BLEClient* BLEDevice::createClient() { + ESP_LOGD(LOG_TAG, ">> createClient"); +#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTC_ENABLE m_pClient = new BLEClient(); + ESP_LOGD(LOG_TAG, "<< createClient"); return m_pClient; } // createClient @@ -59,6 +65,10 @@ bool initialized = false; // Have we been initialized? */ /* STATIC */ BLEServer* BLEDevice::createServer() { ESP_LOGD(LOG_TAG, ">> createServer"); +#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE m_pServer = new BLEServer(); m_pServer->createApp(0); ESP_LOGD(LOG_TAG, "<< createServer"); @@ -260,17 +270,21 @@ bool initialized = false; // Have we been initialized? return; } +#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } +#endif // CONFIG_GATTC_ENABLE +#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } +#endif // CONFIG_GATTS_ENABLE errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); if (errRc != ESP_OK) { From a6cc232e59f0e89a9b05dfacc76a8e0ad7d06a0d Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 19:35:39 +0100 Subject: [PATCH 125/310] Security redesign + examples --- cpp_utils/BLEClient.cpp | 51 ------ cpp_utils/BLEClient.h | 4 - cpp_utils/BLEDescriptor.cpp | 3 + cpp_utils/BLEDescriptor.h | 2 + cpp_utils/BLEDevice.cpp | 102 ++++++++++-- cpp_utils/BLEDevice.h | 4 + cpp_utils/BLESecurity.cpp | 18 ++- cpp_utils/BLESecurity.h | 7 +- cpp_utils/BLEServer.cpp | 61 ------- cpp_utils/BLEServer.h | 7 - .../security/SampleClient_Encryption.cpp | 122 ++++++++++++++ ...nt_authentication_numeric_confirmation.cpp | 153 ++++++++++++++++++ .../SampleClient_authentication_passkey.cpp | 152 +++++++++++++++++ .../BLETests/security/SampleSecureClient.cpp | 147 +++++++++++++++++ .../BLETests/security/SampleSecureServer.cpp | 117 ++++++++++++++ .../security/SampleServer_Encryption.cpp | 85 ++++++++++ ...er_authentication_numeric_confirmation.cpp | 129 +++++++++++++++ .../SampleServer_authentication_passkey.cpp | 132 +++++++++++++++ .../security/SampleServer_authorization.cpp | 132 +++++++++++++++ .../BLETests/security/app_main_security.cpp | 33 ++++ 20 files changed, 1321 insertions(+), 140 deletions(-) create mode 100644 cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleSecureClient.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleSecureServer.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp create mode 100644 cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp create mode 100644 cpp_utils/tests/BLETests/security/app_main_security.cpp diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 3db0be64..55af054c 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -168,15 +168,6 @@ void BLEClient::gattClientEventHandler( break; } // ESP_GATTC_DISCONNECT_EVT - case ESP_GATTS_CONNECT_EVT: { - //m_connId = param->connect.conn_id; // Save the connection id. - if(m_securityLevel){ - esp_ble_set_encryption(evtParam->connect.remote_bda, m_securityLevel); - //memcpy(m_remote_bda, param->connect.remote_bda, sizeof(m_remote_bda)); - } - break; - } // ESP_GATTS_CONNECT_EVT - // // ESP_GATTC_OPEN_EVT // @@ -419,41 +410,6 @@ void BLEClient::handleGAPEvent( break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); - // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); - assert(m_securityCallbacks!=nullptr); - // esp_ble_passkey_reply(m_remote_bda, true, m_securityCallbacks->onPassKeyRequest()); - break; - - /* - * TODO should we add white/black list comparison? - */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - if(m_securityCallbacks!=nullptr) - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, m_securityCallbacks->onSecurityRequest()); - else - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, false); - break; - /* - * - */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - ///show the passkey number to the user to input it in the peer deivce. - if(m_securityCallbacks!=nullptr) - m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key info share with peer device to the user. - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - if(m_securityCallbacks!=nullptr) - m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - break; - default: break; } @@ -491,13 +447,6 @@ void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::s ESP_LOGD(LOG_TAG, "<< setValue"); } // setValue -void BLEClient::setEncryptionLevel(esp_ble_sec_act_t level) { - m_securityLevel = level; -} - -void BLEClient::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { - m_securityCallbacks = callbacks; -} /** * @brief Return a string representation of this client. diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 1a630dfe..a60ed102 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -49,8 +49,6 @@ class BLEClient { void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. - void setEncryptionLevel(esp_ble_sec_act_t level); - void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); std::string toString(); // Return a string representation of this client. @@ -82,8 +80,6 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; void clearServices(); // Clear any existing services. - esp_ble_sec_act_t m_securityLevel = (esp_ble_sec_act_t)0; - BLESecurityCallbacks* m_securityCallbacks = 0; }; // class BLEDevice diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 054ef420..ebccf0d9 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -301,6 +301,9 @@ void BLEDescriptor::setValue(std::string value) { setValue((uint8_t *)value.data(), value.length()); } // setValue +void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) { + m_permissions = perm; +} /** * @brief Return a string representation of the descriptor. diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index ef716d42..9ec408e5 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -39,6 +39,7 @@ class BLEDescriptor { void setCallbacks(BLEDescriptorCallbacks* pCallbacks); void setValue(uint8_t* data, size_t size); void setValue(std::string value); + void setAccessPermissions(esp_gatt_perm_t perm); std::string toString(); private: @@ -53,6 +54,7 @@ class BLEDescriptor { void executeCreate(BLECharacteristic* pCharacteristic); void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; }; /** diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 0f50148b..62d7b910 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -33,7 +33,6 @@ static const char* LOG_TAG = "BLEDevice"; - /** * Singletons for the BLEDevice. */ @@ -41,6 +40,8 @@ BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; bool initialized = false; // Have we been initialized? +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; +BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; /** @@ -94,6 +95,20 @@ bool initialized = false; // Have we been initialized? BLEUtils::dumpGattServerEvent(event, gatts_if, param); + switch(event) { + case ESP_GATTS_CONNECT_EVT: { + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } + break; + } // ESP_GATTS_CONNECT_EVT + + default: { + break; + } + } // switch + + if (BLEDevice::m_pServer != nullptr) { BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); } @@ -117,13 +132,20 @@ bool initialized = false; // Have we been initialized? ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); BLEUtils::dumpGattClientEvent(event, gattc_if, param); -/* + switch(event) { + case ESP_GATTC_CONNECT_EVT: { + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } + break; + } // ESP_GATTC_CONNECT_EVT + default: { break; } } // switch - */ + // If we have a client registered, call it. if (BLEDevice::m_pClient != nullptr) { @@ -143,14 +165,63 @@ bool initialized = false; // Have we been initialized? BLEUtils::dumpGapEvent(event, param); switch(event) { - case ESP_GAP_BLE_SEC_REQ_EVT: { - esp_err_t errRc = ::esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_security_rsp: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); } break; - } - + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); + } + else{ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + } + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer deivce. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); + if(BLEDevice::m_securityCallbacks!=nullptr){ + ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + } + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + if(BLEDevice::m_securityCallbacks!=nullptr){ + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } + break; default: { break; } @@ -167,6 +238,12 @@ bool initialized = false; // Have we been initialized? if (BLEDevice::m_pScan != nullptr) { BLEDevice::getScan()->handleGAPEvent(event, param); } + + /* + * Security events: + */ + + } // gapEventHandler @@ -379,4 +456,11 @@ void BLEDevice::whiteListRemove(BLEAddress address) { ESP_LOGD(LOG_TAG, "<< whiteListRemove"); } // whiteListRemove +void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { + BLEDevice::m_securityLevel = level; +} + +void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + BLEDevice::m_securityCallbacks = callbacks; +} #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index c6383720..28889611 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -38,11 +38,15 @@ class BLEDevice { static std::string toString(); // Return a string representation of our device. static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list. static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list. + static void setEncryptionLevel(esp_ble_sec_act_t level); + static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); private: static BLEServer *m_pServer; static BLEScan *m_pScan; static BLEClient *m_pClient; + static esp_ble_sec_act_t m_securityLevel; + static BLESecurityCallbacks* m_securityCallbacks; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp index f3e5bbe9..273ec77f 100644 --- a/cpp_utils/BLESecurity.cpp +++ b/cpp_utils/BLESecurity.cpp @@ -31,23 +31,29 @@ void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { * @brief Init encryption key by server * @param key_size is value between 7 and 16 */ -void BLESecurity::setInitEncryptionKey(uint8_t init_key, uint8_t key_size) { +void BLESecurity::setInitEncryptionKey(uint8_t init_key) { m_initKey = init_key; - m_keySize = key_size; esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); - esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); // <--- setup encryption key max size } /* * @brief Init encryption key by client * @param key_size is value between 7 and 16 */ -void BLESecurity::setRespEncryptionKey(uint8_t resp_key, uint8_t key_size) { +void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { m_respKey = resp_key; - m_keySize = key_size; esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); - esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); // <--- setup encryption key max size } + +/* + * + * + */ +void BLESecurity::setKeySize(uint8_t key_size) { + m_keySize = key_size; + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); +} + /* * @brief Debug function to display what keys are exchanged by peers */ diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index 494a1b7c..1a649283 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -15,8 +15,9 @@ class BLESecurity { virtual ~BLESecurity(); void setAuthenticationMode(esp_ble_auth_req_t auth_req); void setCapability(esp_ble_io_cap_t iocap); - void setInitEncryptionKey(uint8_t init_key, uint8_t key_size = 16); - void setRespEncryptionKey(uint8_t resp_key, uint8_t key_size = 16); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); static char* esp_key_type_to_str(esp_ble_key_type_t key_type); private: @@ -55,6 +56,8 @@ class BLESecurityCallbacks { * Provide us information when authentication process is completed */ virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t); + + virtual bool onConfirmPIN(uint32_t pin); }; #endif /* COMPONENTS_CPP_UTILS_BLESECURITY_H_ */ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index a3ea7681..8dd2a210 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -151,52 +151,6 @@ void BLEServer::handleGAPEvent( */ break; } - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); - break; - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); - break; - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); - break; - case ESP_GAP_BLE_NC_REQ_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); - break; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); - esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); - assert(m_securityCallbacks!=nullptr); - esp_ble_passkey_reply(m_remote_bda, true, m_securityCallbacks->onPassKeyRequest()); - break; - - /* - * TODO should we add white/black list comparison? - */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - if(m_securityCallbacks!=nullptr) - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, m_securityCallbacks->onSecurityRequest()); - else - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, false); - break; - /* - * - */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - ///show the passkey number to the user to input it in the peer deivce. - if(m_securityCallbacks!=nullptr) - m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key info share with peer device to the user. - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - if(m_securityCallbacks!=nullptr) - m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - break; default: break; @@ -245,10 +199,6 @@ void BLEServer::handleGATTServerEvent( // case ESP_GATTS_CONNECT_EVT: { m_connId = param->connect.conn_id; // Save the connection id. - if(m_securityLevel){ - esp_ble_set_encryption(param->connect.remote_bda, m_securityLevel); - memcpy(m_remote_bda, param->connect.remote_bda, sizeof(m_remote_bda)); - } if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); } @@ -385,17 +335,6 @@ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising -void BLEServer::setEncryptionLevel(esp_ble_sec_act_t level) { - m_securityLevel = level; -} - -uint32_t BLEServer::getPassKey() { - return m_securityPassKey; -} - -void BLEServer::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { - m_securityCallbacks = callbacks; -} void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 8d4554d2..bc0ef05d 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -58,9 +58,6 @@ class BLEServer { BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); - void setEncryptionLevel(esp_ble_sec_act_t level); - uint32_t getPassKey(); - void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); private: @@ -78,10 +75,6 @@ class BLEServer { FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); BLEServiceMap m_serviceMap; BLEServerCallbacks* m_pServerCallbacks; - esp_ble_sec_act_t m_securityLevel = (esp_ble_sec_act_t)0; - esp_bd_addr_t m_remote_bda; - uint32_t m_securityPassKey; - BLESecurityCallbacks* m_securityCallbacks; void createApp(uint16_t appId); uint16_t getConnId(); diff --git a/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp b/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp new file mode 100644 index 00000000..2ab4c909 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp @@ -0,0 +1,122 @@ +/* + * SampleClient_Encryption.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClient_Encryption(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + + diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp new file mode 100644 index 00000000..6d00dc78 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp @@ -0,0 +1,153 @@ +/* + * SampleClient_authentication_numeric_confirmation.cpp + * + * Created on: Dec 23, 2017 + * Author: esp32 + */ + + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); +// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClient_authentication_numeric_confirmation(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp new file mode 100644 index 00000000..8ab3edfd --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp @@ -0,0 +1,152 @@ +/* + * SampleClient_authentication_passkey.cpp + * + * Created on: Dec 23, 2017 + * Author: esp32 + */ + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return false; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); +// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleSecureClient(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + + diff --git a/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp b/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp new file mode 100644 index 00000000..88764720 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp @@ -0,0 +1,147 @@ +/** + * Create a sample BLE client that connects to a BLE server and then retrieves the current + * characteristic value. It will then periodically update the value of the characteristic on the + * remote server with the current time since boot. + */ +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return false; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); +// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleSecureClient(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient diff --git a/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp b/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp new file mode 100644 index 00000000..d1ed296a --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp @@ -0,0 +1,117 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + vTaskDelay(5000); + + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class bleCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new bleCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleSecureServer(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main diff --git a/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp b/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp new file mode 100644 index 00000000..10c16218 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp @@ -0,0 +1,85 @@ +/* + * SampleServer_Encryption.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MyCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->setCallbacks(new MyCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_Encryption(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp new file mode 100644 index 00000000..71728619 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp @@ -0,0 +1,129 @@ +/* + * SampleServer_authentication_numeric_confirmation.cpp + * + * Created on: Dec 23, 2017 + * Author: esp32 + */ + + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class bleCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new bleCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + //pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_authentication_numeric_confirmation(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp new file mode 100644 index 00000000..af9615c3 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp @@ -0,0 +1,132 @@ +/* + * SampleServer_authentication_passkey.cpp + * + * Created on: Dec 23, 2017 + * Author: esp32 + */ + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + /* + * @return value is a pass key from input panel and displayed on peer device + */ + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + /* + * pass_key should be displayed to end user to authenticate with peer device + */ + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + /* + * here we can + */ + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class MyCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new MyCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleSecureServer(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp new file mode 100644 index 00000000..127ed81d --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp @@ -0,0 +1,132 @@ +/* + * SampleServer_authorization.cpp + * + * Created on: Dec 23, 2017 + * Author: esp32 + */ + + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t){ + return false; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class bleCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); +// BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + /* + * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests + */ + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new bleCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + /* + * Authorized permission to read/write descriptor to protect notify/indicate requests + */ + p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); +// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); +/* pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +*/ + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_Authorization(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + diff --git a/cpp_utils/tests/BLETests/security/app_main_security.cpp b/cpp_utils/tests/BLETests/security/app_main_security.cpp new file mode 100644 index 00000000..7b1bbca7 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/app_main_security.cpp @@ -0,0 +1,33 @@ +/** + * Main file for running the BLE samples. + */ +extern "C" { + void app_main(void); +} + + +// The list of sample entry points. + +void SampleClient_Encryption(void); +void SampleServer_Encryption(void); +void SampleClient_authentication_passkey(void); +void SampleServer_authentication_passkey(void); +void SampleClient_authentication_numeric_confirmation(void); +void SampleServer_authentication_numeric_confirmation(void); + +void SampleServer_Authorization(void); + +// +// Un-comment ONE of the following +// --- +void app_main(void) { + + SampleClient_Encryption(); +// SampleServer_Encryption(); +// SampleClient_authentication_passkey(); +// SampleServer_authentication_passkey(); +// SampleClient_authentication_numeric_confirmation(); +// SampleServer_authentication_numeric_confirmation(); +// +// SampleServer_Authorization(); +} // app_main From 6057871db7af7270d2dffa4c95764c23349f1fea Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 23 Dec 2017 12:38:56 -0600 Subject: [PATCH 126/310] Fixes for #310 --- cpp_utils/HttpRequest.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index bbbdce6d..9261af60 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -205,6 +205,7 @@ std::string HttpRequest::getPath() { #define STATE_NAME 0 #define STATE_VALUE 1 + /** * @brief Get the query part of the request. * The query is a set of name = value pairs. The return is a map keyed by the name items. @@ -215,7 +216,15 @@ std::map HttpRequest::getQuery() { // Walk through all the characters in the query string maintaining a simple state machine // that lets us know what we are parsing. std::map queryMap; - std::string queryString = ""; + + std::string possibleQueryString = getPath(); + int qindex = possibleQueryString.find_first_of("?") ; + if (qindex < 0) { + ESP_LOGD(LOG_TAG, "No query string present") ; + return queryMap ; + } + std::string queryString = possibleQueryString.substr(qindex + 1, -1) ; + ESP_LOGD(LOG_TAG, "query string: %s", queryString.c_str()) ; /* * We maintain a simple state machine with states of: From 67671174daa136c2e7cdb6dc5c316ec1b5e79495 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:44:50 +0100 Subject: [PATCH 127/310] add arduino examples --- cpp_utils/HIDKeyboardTypes.h | 402 ++++++++++++++++++ cpp_utils/Makefile.arduino | 8 +- .../BLE_client/BLE_client_encrypted.ino | 138 ++++++ .../BLE_client_encrypted.ino | 138 ++++++ .../BLE_client_numeric_confirmation.ino | 169 ++++++++ .../BLE_client_numeric_confirmation.ino | 169 ++++++++ .../BLE_client/BLE_client_passkey.ino | 168 ++++++++ .../BLE_client_passkey/BLE_client_passkey.ino | 168 ++++++++ .../BLE_server/BLE_server_authorization.ino | 44 ++ .../BLE_server_authorization.ino | 44 ++ .../BLE_server/BLE_server_encrypted.ino | 43 ++ .../BLE_server_encrypted.ino | 43 ++ .../BLE_server_numeric_confirmation.ino | 75 ++++ .../BLE_server_numeric_confirmation.ino | 75 ++++ .../BLE_server/BLE_server_passkey.ino | 74 ++++ .../BLE_server_passkey/BLE_server_passkey.ino | 74 ++++ ...nt_authentication_numeric_confirmation.cpp | 4 +- .../SampleClient_authentication_passkey.cpp | 6 +- .../BLETests/security/SampleSecureClient.cpp | 147 ------- .../BLETests/security/SampleSecureServer.cpp | 117 ----- ...er_authentication_numeric_confirmation.cpp | 2 +- .../SampleServer_authentication_passkey.cpp | 14 +- .../security/SampleServer_authorization.cpp | 13 +- 23 files changed, 1846 insertions(+), 289 deletions(-) create mode 100644 cpp_utils/HIDKeyboardTypes.h create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino delete mode 100644 cpp_utils/tests/BLETests/security/SampleSecureClient.cpp delete mode 100644 cpp_utils/tests/BLETests/security/SampleSecureServer.cpp diff --git a/cpp_utils/HIDKeyboardTypes.h b/cpp_utils/HIDKeyboardTypes.h new file mode 100644 index 00000000..ef48a526 --- /dev/null +++ b/cpp_utils/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index f9239250..82d7da3e 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -28,6 +28,8 @@ BLE_FILES= \ BLEDevice.h \ BLEExceptions.cpp \ BLEExceptions.h \ + BLEHIDDevice.cpp \ + BLEHIDDevice.h \ BLERemoteCharacteristic.cpp \ BLERemoteCharacteristic.h \ BLERemoteDescriptor.cpp \ @@ -41,6 +43,8 @@ BLE_FILES= \ BLEService.cpp \ BLEService.h \ BLEServiceMap.cpp \ + BLESecurity.cpp \ + BLESecurity.h \ BLEUtils.cpp \ BLEUtils.h \ BLEUUID.cpp \ @@ -50,7 +54,9 @@ BLE_FILES= \ FreeRTOS.h \ FreeRTOS.cpp \ GeneralUtils.h \ - GeneralUtils.cpp + GeneralUtils.cpp \ + HIDTypes.h \ + HIDKeyboardTypes.h ARDUINO_LIBS=$(HOME)/Arduino/libraries diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino new file mode 100644 index 00000000..e77d774f --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino @@ -0,0 +1,138 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino new file mode 100644 index 00000000..e77d774f --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino @@ -0,0 +1,138 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino new file mode 100644 index 00000000..846a664b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino @@ -0,0 +1,169 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino new file mode 100644 index 00000000..846a664b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino @@ -0,0 +1,169 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino new file mode 100644 index 00000000..763a87d2 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino @@ -0,0 +1,168 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino new file mode 100644 index 00000000..763a87d2 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino @@ -0,0 +1,168 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino new file mode 100644 index 00000000..7da509b4 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino @@ -0,0 +1,44 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + /* + * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests + */ + + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino new file mode 100644 index 00000000..7da509b4 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino @@ -0,0 +1,44 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + /* + * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests + */ + + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino new file mode 100644 index 00000000..e712794b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino @@ -0,0 +1,43 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino new file mode 100644 index 00000000..e712794b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino @@ -0,0 +1,43 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino new file mode 100644 index 00000000..d726104b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino @@ -0,0 +1,75 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino new file mode 100644 index 00000000..d726104b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino @@ -0,0 +1,75 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino new file mode 100644 index 00000000..a79a305a --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino @@ -0,0 +1,74 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino new file mode 100644 index 00000000..a79a305a --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino @@ -0,0 +1,74 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("MyESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp index 6d00dc78..2791f3d1 100644 --- a/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp @@ -2,7 +2,7 @@ * SampleClient_authentication_numeric_confirmation.cpp * * Created on: Dec 23, 2017 - * Author: esp32 + * Author: chegewara */ @@ -71,7 +71,7 @@ class MyClient: public Task { BLESecurity *pSecurity = new BLESecurity(); pSecurity->setKeySize(); -// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); pSecurity->setCapability(ESP_IO_CAP_IO); pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); // Connect to the remove BLE Server. diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp index 8ab3edfd..4848385d 100644 --- a/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp @@ -2,7 +2,7 @@ * SampleClient_authentication_passkey.cpp * * Created on: Dec 23, 2017 - * Author: esp32 + * Author: chegewara */ #include @@ -69,8 +69,8 @@ class MyClient: public Task { BLEDevice::setSecurityCallbacks(new MySecurity()); BLESecurity *pSecurity = new BLESecurity(); -// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); // Connect to the remove BLE Server. pClient->connect(*pAddress); diff --git a/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp b/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp deleted file mode 100644 index 88764720..00000000 --- a/cpp_utils/tests/BLETests/security/SampleSecureClient.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Create a sample BLE client that connects to a BLE server and then retrieves the current - * characteristic value. It will then periodically update the value of the characteristic on the - * remote server with the current time since boot. - */ -#include -#include -#include -#include -#include "BLEDevice.h" - -#include "BLEAdvertisedDevice.h" -#include "BLEClient.h" -#include "BLEScan.h" -#include "BLEUtils.h" -#include "Task.h" - -#include "sdkconfig.h" - -static const char* LOG_TAG = "SampleClient"; - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ -// The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); -// The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onConfirmPIN(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); - vTaskDelay(5000); - return false; - } - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "Security Request"); - return true; - } - void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ - if(auth_cmpl.success){ - ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); - esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); - ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); - } - ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); - } -}; - - -/** - * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server - * as the input parameter when the task is created. - */ -class MyClient: public Task { - void run(void* data) { - - BLEAddress* pAddress = (BLEAddress*)data; - BLEClient* pClient = BLEDevice::createClient(); - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - BLEDevice::setSecurityCallbacks(new MySecurity()); - - BLESecurity *pSecurity = new BLESecurity(); -// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_IO); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - // Connect to the remove BLE Server. - pClient->connect(*pAddress); - - // Obtain a reference to the service we are after in the remote BLE server. - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); - if (pRemoteService == nullptr) { - ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); - return; - } - - - // Obtain a reference to the characteristic in the service of the remote BLE server. - BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); - if (pRemoteCharacteristic == nullptr) { - ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); - return; - } - - // Read the value of the characteristic. - std::string value = pRemoteCharacteristic->readValue(); - ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); - - while(1) { - // Set a new value of the characteristic - ESP_LOGD(LOG_TAG, "Setting the new value"); - std::ostringstream stringStream; - struct timeval tv; - gettimeofday(&tv, nullptr); - stringStream << "Time since boot: " << tv.tv_sec; - pRemoteCharacteristic->writeValue(stringStream.str()); - - FreeRTOS::sleep(1000); - } - - pClient->disconnect(); - - ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); - ESP_LOGD(LOG_TAG, "-- End of task"); - } // run -}; // MyClient - - -/** - * Scan for BLE servers and find the first one that advertises the service we are looking for. - */ -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - void onResult(BLEAdvertisedDevice advertisedDevice) { - ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); - - if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { - advertisedDevice.getScan()->stop(); - - ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); - MyClient* pMyClient = new MyClient(); - pMyClient->setStackSize(18000); - pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); - } // Found our server - } // onResult -}; // MyAdvertisedDeviceCallbacks - - -/** - * Perform the work of a sample BLE client. - */ -void SampleSecureClient(void) { - ESP_LOGD(LOG_TAG, "Scanning sample starting"); - BLEDevice::init(""); - BLEScan *pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setActiveScan(true); - pBLEScan->start(5); -} // SampleClient diff --git a/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp b/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp deleted file mode 100644 index d1ed296a..00000000 --- a/cpp_utils/tests/BLETests/security/SampleSecureServer.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Create a new BLE server. - */ -#include "BLEDevice.h" -#include "BLEServer.h" -#include "BLEUtils.h" -#include "BLE2902.h" -#include -#include -#include - - -#include "sdkconfig.h" - -static char LOG_TAG[] = "SampleServer"; - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - ESP_LOGI(LOG_TAG, "PassKeyRequest"); - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onConfirmPIN(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); - vTaskDelay(5000); - return true; - } - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "SecurityRequest"); - vTaskDelay(5000); - - return true; - } - - void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ - ESP_LOGI(LOG_TAG, "Starting BLE work!"); - if(cmpl.success){ - uint16_t length; - esp_ble_gap_get_whitelist_size(&length); - ESP_LOGD(LOG_TAG, "size: %d", length); - } - } -}; - -class bleCharacteristicCallback: public BLECharacteristicCallbacks { - void onWrite(BLECharacteristic* pCharacteristic) { - std::string msg = pCharacteristic->getValue(); - ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); - esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); - esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); - } - - void onRead(BLECharacteristic* pCharacteristic) { - std::string msg = pCharacteristic->getValue(); - ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); - esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); - esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); - } -}; -class MainBLEServer: public Task { - void run(void *data) { - ESP_LOGD(LOG_TAG, "Starting BLE work!"); - esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); - esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); - BLEDevice::init("ESP32"); - BLEServer* pServer = BLEDevice::createServer(); - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); - BLEDevice::setSecurityCallbacks(new MySecurity()); - - BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); - - BLECharacteristic* pCharacteristic = pService->createCharacteristic( - BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_INDICATE - ); - pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - pCharacteristic->setCallbacks(new bleCharacteristicCallback()); - pCharacteristic->setValue("Hello World!"); - - BLE2902* p2902Descriptor = new BLE2902(); - p2902Descriptor->setNotifications(true); - pCharacteristic->addDescriptor(p2902Descriptor); - p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - - pService->start(); - - BLEAdvertising* pAdvertising = pServer->getAdvertising(); - pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); - - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_IO); - pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - - pAdvertising->start(); - - ESP_LOGD(LOG_TAG, "Advertising started!"); - delay(1000000); - } -}; - - -void SampleSecureServer(void) -{ - //esp_log_level_set("*", ESP_LOG_DEBUG); - MainBLEServer* pMainBleServer = new MainBLEServer(); - pMainBleServer->setStackSize(20000); - pMainBleServer->start(); - -} // app_main diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp index 71728619..3231b37d 100644 --- a/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp @@ -2,7 +2,7 @@ * SampleServer_authentication_numeric_confirmation.cpp * * Created on: Dec 23, 2017 - * Author: esp32 + * Author: chegewara */ diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp index af9615c3..24d45868 100644 --- a/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp @@ -2,7 +2,7 @@ * SampleServer_authentication_passkey.cpp * * Created on: Dec 23, 2017 - * Author: esp32 + * Author: chegewara */ /** @@ -23,22 +23,15 @@ static char LOG_TAG[] = "SampleServer"; class MySecurity : public BLESecurityCallbacks { - /* - * @return value is a pass key from input panel and displayed on peer device - */ uint32_t onPassKeyRequest(){ ESP_LOGI(LOG_TAG, "PassKeyRequest"); return 123456; } - /* - * pass_key should be displayed to end user to authenticate with peer device - */ + void onPassKeyNotify(uint32_t pass_key){ ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); } - /* - * here we can - */ + bool onSecurityRequest(){ ESP_LOGI(LOG_TAG, "On Security Request"); return true; @@ -109,7 +102,6 @@ class MainBLEServer: public Task { pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); pSecurity->setCapability(ESP_IO_CAP_OUT); pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); pAdvertising->start(); diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp index 127ed81d..02e47e4d 100644 --- a/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp +++ b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp @@ -2,7 +2,7 @@ * SampleServer_authorization.cpp * * Created on: Dec 23, 2017 - * Author: esp32 + * Author: chegewara */ @@ -32,7 +32,7 @@ class MySecurity : public BLESecurityCallbacks { ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); } bool onConfirmPIN(uint32_t){ - return false; + return true; } bool onSecurityRequest(){ ESP_LOGI(LOG_TAG, "On Security Request"); @@ -107,11 +107,10 @@ class MainBLEServer: public Task { pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); BLESecurity *pSecurity = new BLESecurity(); -// pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_OUT); -/* pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); -*/ + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_NONE); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + pAdvertising->start(); ESP_LOGD(LOG_TAG, "Advertising started!"); From 5251054d02e79ca811b3e822542efd8d3e46f45c Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:47:56 +0100 Subject: [PATCH 128/310] Delete BLE_server_passkey.ino --- .../BLE_server/BLE_server_passkey.ino | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino deleted file mode 100644 index a79a305a..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey.ino +++ /dev/null @@ -1,74 +0,0 @@ -/* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp - Ported to Arduino ESP32 by Evandro Copercini -*/ - -#include -#include -#include - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ - -#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" -#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - ESP_LOGI(LOG_TAG, "PassKeyRequest"); - return 123456; - } - - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); - } - - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "On Security Request"); - return true; - } - - void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ - ESP_LOGI(LOG_TAG, "Starting BLE work!"); - if(cmpl.success){ - uint16_t length; - esp_ble_gap_get_whitelist_size(&length); - ESP_LOGD(LOG_TAG, "size: %d", length); - } - } -}; - -void setup() { - Serial.begin(115200); - Serial.println("Starting BLE work!"); - - BLEDevice::init("MyESP32"); - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - /* - * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation - */ - BLEDevice::setSecurityCallbacks(new MySecurity()); - BLEServer *pServer = BLEDevice::createServer(); - BLEService *pService = pServer->createService(SERVICE_UUID); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE - ); - - pCharacteristic->setValue("Hello World says Neil"); - pService->start(); - BLEAdvertising *pAdvertising = pServer->getAdvertising(); - pAdvertising->start(); - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_OUT); - pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - Serial.println("Characteristic defined! Now you can read it in your phone!"); -} - -void loop() { - // put your main code here, to run repeatedly: - delay(2000); -} \ No newline at end of file From f85d4d4eee1d01c7de27a1eb2121d091fa0ad8f0 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:48:04 +0100 Subject: [PATCH 129/310] Delete BLE_server_numeric_confirmation.ino --- .../BLE_server_numeric_confirmation.ino | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino deleted file mode 100644 index d726104b..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation.ino +++ /dev/null @@ -1,75 +0,0 @@ -/* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp - Ported to Arduino ESP32 by Evandro Copercini -*/ - -#include -#include -#include - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ - -#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" -#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - ESP_LOGI(LOG_TAG, "PassKeyRequest"); - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onConfirmPIN(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); - vTaskDelay(5000); - return true; - } - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "SecurityRequest"); - return true; - } - - void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ - ESP_LOGI(LOG_TAG, "Starting BLE work!"); - } -}; - -void setup() { - Serial.begin(115200); - Serial.println("Starting BLE work!"); - - BLEDevice::init("MyESP32"); - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - /* - * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation - */ - BLEDevice::setSecurityCallbacks(new MySecurity()); - BLEServer *pServer = BLEDevice::createServer(); - BLEService *pService = pServer->createService(SERVICE_UUID); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE - ); - - pCharacteristic->setValue("Hello World says Neil"); - pService->start(); - BLEAdvertising *pAdvertising = pServer->getAdvertising(); - pAdvertising->start(); - - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setKeySize(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_IO); - pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - - Serial.println("Characteristic defined! Now you can read it in your phone!"); -} - -void loop() { - // put your main code here, to run repeatedly: - delay(2000); -} \ No newline at end of file From 924b0d2d603c955f6e55611d298c9dcce50d690d Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:48:12 +0100 Subject: [PATCH 130/310] Delete BLE_server_encrypted.ino --- .../BLE_server/BLE_server_encrypted.ino | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino deleted file mode 100644 index e712794b..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted.ino +++ /dev/null @@ -1,43 +0,0 @@ -/* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp - Ported to Arduino ESP32 by Evandro Copercini -*/ - -#include -#include -#include - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ - -#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" -#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" - -void setup() { - Serial.begin(115200); - Serial.println("Starting BLE work!"); - - BLEDevice::init("MyESP32"); - /* - * Here we have implemented simplest security. This kind security does not provide authentication - */ - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - BLEServer *pServer = BLEDevice::createServer(); - BLEService *pService = pServer->createService(SERVICE_UUID); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE - ); - - pCharacteristic->setValue("Hello World says Neil"); - pService->start(); - BLEAdvertising *pAdvertising = pServer->getAdvertising(); - pAdvertising->start(); - Serial.println("Characteristic defined! Now you can read it in your phone!"); -} - -void loop() { - // put your main code here, to run repeatedly: - delay(2000); -} \ No newline at end of file From 38d712e7afe8201a9cfd7feb756f949702f8b8f8 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:48:20 +0100 Subject: [PATCH 131/310] Delete BLE_server_authorization.ino --- .../BLE_server/BLE_server_authorization.ino | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino deleted file mode 100644 index 7da509b4..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization.ino +++ /dev/null @@ -1,44 +0,0 @@ -/* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp - Ported to Arduino ESP32 by Evandro Copercini -*/ - -#include -#include -#include - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ - -#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" -#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" - -void setup() { - Serial.begin(115200); - Serial.println("Starting BLE work!"); - - BLEDevice::init("MyESP32"); - BLEServer *pServer = BLEDevice::createServer(); - BLEService *pService = pServer->createService(SERVICE_UUID); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE - ); - - /* - * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests - */ - - pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - pCharacteristic->setValue("Hello World says Neil"); - pService->start(); - BLEAdvertising *pAdvertising = pServer->getAdvertising(); - pAdvertising->start(); - Serial.println("Characteristic defined! Now you can read it in your phone!"); -} - -void loop() { - // put your main code here, to run repeatedly: - delay(2000); -} \ No newline at end of file From 2d9422c035c077dfe3ce0bb56dceeb6282d5ef9a Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:48:58 +0100 Subject: [PATCH 132/310] Update BLE_server_passkey.ino --- .../BLE_server/BLE_server_passkey/BLE_server_passkey.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino index a79a305a..a48ad482 100644 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino @@ -43,7 +43,7 @@ void setup() { Serial.begin(115200); Serial.println("Starting BLE work!"); - BLEDevice::init("MyESP32"); + BLEDevice::init("ESP32"); BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); /* * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation @@ -71,4 +71,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} From 48d9031be9a817910769ad541e711611a6083ced Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:49:25 +0100 Subject: [PATCH 133/310] Update BLE_server_numeric_confirmation.ino --- .../BLE_server_numeric_confirmation.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino index d726104b..186019e8 100644 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino @@ -41,7 +41,7 @@ void setup() { Serial.begin(115200); Serial.println("Starting BLE work!"); - BLEDevice::init("MyESP32"); + BLEDevice::init("ESP32"); BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); /* * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation @@ -72,4 +72,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} From 9d4e62fefce34cb30cee4567d5cefd8a8f56e98d Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:49:47 +0100 Subject: [PATCH 134/310] Update BLE_server_encrypted.ino --- .../BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino index e712794b..43ffd2c9 100644 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino @@ -17,7 +17,7 @@ void setup() { Serial.begin(115200); Serial.println("Starting BLE work!"); - BLEDevice::init("MyESP32"); + BLEDevice::init("ESP32"); /* * Here we have implemented simplest security. This kind security does not provide authentication */ @@ -40,4 +40,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} From 8f0d70774d755b387209c657002417bceb4c655a Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 23 Dec 2017 21:50:08 +0100 Subject: [PATCH 135/310] Update BLE_server_authorization.ino --- .../BLE_server_authorization/BLE_server_authorization.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino index 7da509b4..75a461af 100644 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino @@ -17,7 +17,7 @@ void setup() { Serial.begin(115200); Serial.println("Starting BLE work!"); - BLEDevice::init("MyESP32"); + BLEDevice::init("ESP32"); BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( @@ -41,4 +41,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} From f28769c8d2e7fd313ed78644d477fcd918c42203 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 23 Dec 2017 16:25:55 -0600 Subject: [PATCH 136/310] Code changes for #314 --- cpp_utils/BLE2904.cpp | 54 ++++++++++++++++++++++++ cpp_utils/BLE2904.h | 74 +++++++++++++++++++++++++++++++++ cpp_utils/BLECharacteristic.cpp | 6 +-- cpp_utils/BLEDescriptor.cpp | 14 +++---- cpp_utils/BLEDescriptor.h | 37 +++++++++-------- 5 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 cpp_utils/BLE2904.cpp create mode 100644 cpp_utils/BLE2904.h diff --git a/cpp_utils/BLE2904.cpp b/cpp_utils/BLE2904.cpp new file mode 100644 index 00000000..f0305cb4 --- /dev/null +++ b/cpp_utils/BLE2904.cpp @@ -0,0 +1,54 @@ +/* + * BLE2904.cpp + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLE2904.h" + + +BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) { + m_data.m_format = 0; + m_data.m_exponent = 0; + m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers + m_data.m_unit = 0; + m_data.m_description = 0; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} // BLE2902 + + +void BLE2904::setDescription(uint16_t description) { + m_data.m_description = description; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} + + +void BLE2904::setExponent(int8_t exponent) { + m_data.m_exponent = exponent; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} // setExponent + +void BLE2904::setFormat(uint8_t format) { + m_data.m_format = format; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} // setFormat + +void BLE2904::setNamespace(uint8_t namespace_value) { + m_data.m_namespace = namespace_value; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} // setNamespace + +void BLE2904::setUnit(uint16_t unit) { + m_data.m_unit = unit; + setValue((uint8_t*)&m_data, sizeof(m_data)); +} // setUnit + +#endif diff --git a/cpp_utils/BLE2904.h b/cpp_utils/BLE2904.h new file mode 100644 index 00000000..ef9a770a --- /dev/null +++ b/cpp_utils/BLE2904.h @@ -0,0 +1,74 @@ +/* + * BLE2904.h + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_ +#define COMPONENTS_CPP_UTILS_BLE2904_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEDescriptor.h" + +struct BLE2904_Data { + uint8_t m_format; + int8_t m_exponent; + uint16_t m_unit; + uint8_t m_namespace; + uint16_t m_description; + +} __attribute__((packed)); + +/** + * @brief Descriptor for Characteristic Presentation Format. + * + * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +class BLE2904: public BLEDescriptor { +public: + BLE2904(); + static const uint8_t FORMAT_BOOLEAN = 1; + static const uint8_t FORMAT_UINT2 = 2; + static const uint8_t FORMAT_UINT4 = 3; + static const uint8_t FORMAT_UINT8 = 4; + static const uint8_t FORMAT_UINT12 = 5; + static const uint8_t FORMAT_UINT16 = 6; + static const uint8_t FORMAT_UINT24 = 7; + static const uint8_t FORMAT_UINT32 = 8; + static const uint8_t FORMAT_UINT48 = 9; + static const uint8_t FORMAT_UINT64 = 10; + static const uint8_t FORMAT_UINT128 = 11; + static const uint8_t FORMAT_SINT8 = 12; + static const uint8_t FORMAT_SINT12 = 13; + static const uint8_t FORMAT_SINT16 = 14; + static const uint8_t FORMAT_SINT24 = 15; + static const uint8_t FORMAT_SINT32 = 16; + static const uint8_t FORMAT_SINT48 = 17; + static const uint8_t FORMAT_SINT64 = 18; + static const uint8_t FORMAT_SINT128 = 19; + static const uint8_t FORMAT_FLOAT32 = 20; + static const uint8_t FORMAT_FLOAT64 = 21; + static const uint8_t FORMAT_SFLOAT16 = 22; + static const uint8_t FORMAT_SFLOAT32 = 23; + static const uint8_t FORMAT_IEEE20601 = 24; + static const uint8_t FORMAT_UTF8 = 25; + static const uint8_t FORMAT_UTF16 = 26; + static const uint8_t FORMAT_OPAQUE = 27; + + void setDescription(uint16_t); + void setExponent(int8_t exponent); + void setFormat(uint8_t format); + void setNamespace(uint8_t namespace_value); + void setUnit(uint16_t unit); + +private: + BLE2904_Data m_data; +}; // BLE2904 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 9e9aa5c6..3f8460ac 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -324,7 +324,6 @@ void BLECharacteristic::handleGATTServerEvent( // - bool need_rsp // case ESP_GATTS_READ_EVT: { - ESP_LOGD(LOG_TAG, "- Testing: 0x%.2x == 0x%.2x", param->read.handle, m_handle); if (param->read.handle == m_handle) { if (m_pCallbacks != nullptr) { @@ -352,10 +351,9 @@ void BLECharacteristic::handleGATTServerEvent( // the logic flow comprehension. // uint16_t maxOffset = m_mtu - 1; - if (m_mtu > 512) + if (m_mtu > 512) { maxOffset = 512; - ESP_LOGI(LOG_TAG, "%d", m_mtu); - ESP_LOGI(LOG_TAG, "%d", maxOffset); + } if (param->read.need_rsp) { ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index ebccf0d9..1a72ef30 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -37,12 +37,12 @@ BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) { */ BLEDescriptor::BLEDescriptor(BLEUUID uuid) { m_bleUUID = uuid; - m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. - m_value.attr_len = 0; - m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; - m_handle = NULL_HANDLE; - m_pCharacteristic = nullptr; // No initial characteristic. - m_pCallback = nullptr; // No initial callback. + m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. + m_value.attr_len = 0; // Initial length is 0. + m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; // Maximum length of the data. + m_handle = NULL_HANDLE; // Handle is initially unknown. + m_pCharacteristic = nullptr; // No initial characteristic. + m_pCallback = nullptr; // No initial callback. } // BLEDescriptor @@ -51,7 +51,7 @@ BLEDescriptor::BLEDescriptor(BLEUUID uuid) { * @brief BLEDescriptor destructor. */ BLEDescriptor::~BLEDescriptor() { - free(m_value.attr_value); + free(m_value.attr_value); // Release the storage we created in the constructor. } // ~BLEDescriptor diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 9ec408e5..d9e0aefb 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -28,34 +28,37 @@ class BLEDescriptor { BLEDescriptor(BLEUUID uuid); virtual ~BLEDescriptor(); - uint16_t getHandle(); - size_t getLength(); - BLEUUID getUUID(); - uint8_t* getValue(); + uint16_t getHandle(); // Get the handle of the descriptor. + size_t getLength(); // Get the length of the value of the descriptor. + BLEUUID getUUID(); // Get the UUID of the descriptor. + uint8_t* getValue(); // Get a pointer to the value of the descriptor. void handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - void setCallbacks(BLEDescriptorCallbacks* pCallbacks); - void setValue(uint8_t* data, size_t size); - void setValue(std::string value); - void setAccessPermissions(esp_gatt_perm_t perm); - std::string toString(); -private: + void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor. + void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. + void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(std::string value); // Set the value of the descriptor as a data buffer. + + std::string toString(); // Convert the descriptor to a string representation. +private: friend class BLEDescriptorMap; friend class BLECharacteristic; - BLEUUID m_bleUUID; - esp_attr_value_t m_value; - uint16_t m_handle; - BLECharacteristic* m_pCharacteristic; + BLEUUID m_bleUUID; + uint16_t m_handle; BLEDescriptorCallbacks* m_pCallback; + BLECharacteristic* m_pCharacteristic; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + esp_attr_value_t m_value; + void executeCreate(BLECharacteristic* pCharacteristic); void setHandle(uint16_t handle); - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; -}; +}; // BLEDescriptor + /** * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. From 7f9ccb51a6994ef2b1d1051b14a08747fee215f5 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 25 Dec 2017 16:24:07 -0600 Subject: [PATCH 137/310] Added reference to assigned numbers for units #314 --- cpp_utils/BLE2904.cpp | 20 ++++++++++++++++++++ cpp_utils/BLE2904.h | 2 +- cpp_utils/BLEUtils.cpp | 6 ++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLE2904.cpp b/cpp_utils/BLE2904.cpp index f0305cb4..4423ed82 100644 --- a/cpp_utils/BLE2904.cpp +++ b/cpp_utils/BLE2904.cpp @@ -25,27 +25,47 @@ BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) { } // BLE2902 +/** + * @brief Set the description. + */ void BLE2904::setDescription(uint16_t description) { m_data.m_description = description; setValue((uint8_t*)&m_data, sizeof(m_data)); } +/** + * @brief Set the exponent. + */ void BLE2904::setExponent(int8_t exponent) { m_data.m_exponent = exponent; setValue((uint8_t*)&m_data, sizeof(m_data)); } // setExponent + +/** + * @brief Set the format. + */ void BLE2904::setFormat(uint8_t format) { m_data.m_format = format; setValue((uint8_t*)&m_data, sizeof(m_data)); } // setFormat + +/** + * @brief Set the namespace. + */ void BLE2904::setNamespace(uint8_t namespace_value) { m_data.m_namespace = namespace_value; setValue((uint8_t*)&m_data, sizeof(m_data)); } // setNamespace + +/** + * @brief Set the units for this value. It should be one of the encoded values defined here: + * https://www.bluetooth.com/specifications/assigned-numbers/units + * @param [in] uint The type of units of this characteristic as defined by assigned numbers. + */ void BLE2904::setUnit(uint16_t unit) { m_data.m_unit = unit; setValue((uint8_t*)&m_data, sizeof(m_data)); diff --git a/cpp_utils/BLE2904.h b/cpp_utils/BLE2904.h index ef9a770a..cb337e22 100644 --- a/cpp_utils/BLE2904.h +++ b/cpp_utils/BLE2904.h @@ -15,7 +15,7 @@ struct BLE2904_Data { uint8_t m_format; int8_t m_exponent; - uint16_t m_unit; + uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units uint8_t m_namespace; uint16_t m_description; diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index f96db1c5..ff4ebfaf 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1611,13 +1611,15 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_status_t status // - uint16_t conn_id // - uint16_t handle + // - uint16_t offset // case ESP_GATTC_WRITE_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x]", + ESP_LOGD(LOG_TAG, "[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.handle, + evtParam->write.offset ); break; } // ESP_GATTC_WRITE_CHAR_EVT From 37a5652be6ff57d427dcf3fb61634fdca66b07f0 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 27 Dec 2017 22:08:35 -0600 Subject: [PATCH 138/310] Fixes for #321 --- cpp_utils/I2C.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index aac369cf..7c8f5056 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -143,7 +143,7 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } - esp_err_t errRc = ::i2c_master_read(m_cmd, bytes, length, !ack); + esp_err_t errRc = ::i2c_master_read(m_cmd, bytes, length, ack?I2C_MASTER_ACK:I2C_MASTER_NACK); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_read: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } @@ -168,7 +168,7 @@ void I2C::read(uint8_t *byte, bool ack) { ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } - ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, !ack)); + ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, ack?I2C_MASTER_ACK:I2C_MASTER_NACK)); } // readByte From da4394cd46f622e153fb39735dd732da647904d8 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 28 Dec 2017 09:44:35 +0100 Subject: [PATCH 139/310] Some mtu related bug fixes --- cpp_utils/BLECharacteristic.cpp | 25 +--- cpp_utils/BLECharacteristic.h | 1 - cpp_utils/BLEDevice.cpp | 42 +++++- cpp_utils/BLEDevice.h | 3 + .../tests/BLETests/SampleSecureClient.cpp | 141 ------------------ .../tests/BLETests/SampleSecureServer.cpp | 88 ----------- 6 files changed, 52 insertions(+), 248 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/SampleSecureClient.cpp delete mode 100644 cpp_utils/tests/BLETests/SampleSecureServer.cpp diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 3f8460ac..52310d7a 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -15,6 +15,7 @@ #include #include "BLECharacteristic.h" #include "BLEService.h" +#include "BLEDevice.h" #include "BLEUtils.h" #include "BLE2902.h" #include "GeneralUtils.h" @@ -350,8 +351,9 @@ void BLECharacteristic::handleGATTServerEvent( // The following code has deliberately not been factored to make it fewer statements because this would cloud the // the logic flow comprehension. // - uint16_t maxOffset = m_mtu - 1; - if (m_mtu > 512) { + // TODO requires some more research to confirm that 512 is max PDU like in bluetooth specs + uint16_t maxOffset = BLEDevice::getMTU() - 1; + if (BLEDevice::getMTU() > 512) { maxOffset = 512; } if (param->read.need_rsp) { @@ -418,7 +420,6 @@ void BLECharacteristic::handleGATTServerEvent( } case ESP_GATTS_CONNECT_EVT: - m_mtu = 23; m_semaphoreConfEvt.give(); break; @@ -426,10 +427,6 @@ void BLECharacteristic::handleGATTServerEvent( m_semaphoreConfEvt.give(); break; - case ESP_GATTS_MTU_EVT : - m_mtu = param->mtu.mtu; - break; - default: { break; } // default @@ -473,14 +470,11 @@ void BLECharacteristic::indicate() { return; } - if (m_value.getValue().length() > 20) { - ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)"); + if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) { + ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum indicate size)", BLEDevice::getMTU() - 3); } size_t length = m_value.getValue().length(); - if (length > 20) { - length = 20; - } m_semaphoreConfEvt.take("indicate"); @@ -529,14 +523,11 @@ void BLECharacteristic::notify() { return; } - if (m_value.getValue().length() > 20) { - ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)"); + if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) { + ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", BLEDevice::getMTU() - 3); } size_t length = m_value.getValue().length(); - if (length > 20) { - length = 20; - } m_semaphoreConfEvt.take("notify"); diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index a65bf121..fefe59a0 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -103,7 +103,6 @@ class BLECharacteristic { BLEService* m_pService; BLEValue m_value; esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; - uint16_t m_mtu = 23; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 62d7b910..a2dd80b4 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -17,6 +17,7 @@ #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 ESP-IDF #include // ESP32 ESP-IDF #include // Part of C++ Standard library @@ -42,7 +43,7 @@ BLEClient* BLEDevice::m_pClient = nullptr; bool initialized = false; // Have we been initialized? esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; - +uint16_t BLEDevice::m_localMTU = 23; /** * @brief Create a new instance of a client. @@ -103,6 +104,11 @@ BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; break; } // ESP_GATTS_CONNECT_EVT + case ESP_GATTS_MTU_EVT: { + BLEDevice::m_localMTU = param->mtu.mtu; + ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); + break; + } default: { break; } @@ -135,6 +141,9 @@ BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; switch(event) { case ESP_GATTC_CONNECT_EVT: { + if(BLEDevice::getMTU() != 23){ + esp_err_t err = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); + } if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } @@ -456,11 +465,42 @@ void BLEDevice::whiteListRemove(BLEAddress address) { ESP_LOGD(LOG_TAG, "<< whiteListRemove"); } // whiteListRemove +/* + * @brief Set encryption level that will be negotiated with peer device durng connection + * @param [in] level Requested encryption level + */ void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { BLEDevice::m_securityLevel = level; } +/* + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback + */ void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { BLEDevice::m_securityCallbacks = callbacks; } + +/* + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer + * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 + */ +esp_err_t BLEDevice::setMTU(uint16_t mtu) { + ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); + if(err == ESP_OK){ + m_localMTU = mtu; + } else { + ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); + } + ESP_LOGD(LOG_TAG, "<< setLocalMTU"); + return err; +} + +/* + * @brief Get local MTU value set during mtu request or default value + */ +uint16_t BLEDevice::getMTU() { + return m_localMTU; +} #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 28889611..7f331435 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -40,6 +40,8 @@ class BLEDevice { static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list. static void setEncryptionLevel(esp_ble_sec_act_t level); static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); + static esp_err_t setMTU(uint16_t mtu); + static uint16_t getMTU(); private: static BLEServer *m_pServer; @@ -47,6 +49,7 @@ class BLEDevice { static BLEClient *m_pClient; static esp_ble_sec_act_t m_securityLevel; static BLESecurityCallbacks* m_securityCallbacks; + static uint16_t m_localMTU; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/tests/BLETests/SampleSecureClient.cpp b/cpp_utils/tests/BLETests/SampleSecureClient.cpp deleted file mode 100644 index 859291cb..00000000 --- a/cpp_utils/tests/BLETests/SampleSecureClient.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Create a sample BLE client that connects to a BLE server and then retrieves the current - * characteristic value. It will then periodically update the value of the characteristic on the - * remote server with the current time since boot. - */ -#include -#include -#include -#include -#include "BLEDevice.h" - -#include "BLEAdvertisedDevice.h" -#include "BLEClient.h" -#include "BLEScan.h" -#include "BLEUtils.h" -#include "Task.h" - -#include "sdkconfig.h" - -static const char* LOG_TAG = "SampleClient"; - -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ -// The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); -// The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onSecurityRequest(){ - return true; - } - void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ - if(auth_cmpl.success){ - ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); - esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); - ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); - } - ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); - } -}; - - -/** - * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server - * as the input parameter when the task is created. - */ -class MyClient: public Task { - void run(void* data) { - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM); - pSecurity->setCapability(ESP_IO_CAP_KBDISP); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - - BLEAddress* pAddress = (BLEAddress*)data; - BLEClient* pClient = BLEDevice::createClient(); - pClient->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - pClient->setSecurityCallbacks(new MySecurity()); - - // Connect to the remove BLE Server. - pClient->connect(*pAddress); - - // Obtain a reference to the service we are after in the remote BLE server. - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); - if (pRemoteService == nullptr) { - ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); - return; - } - - - // Obtain a reference to the characteristic in the service of the remote BLE server. - BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); - if (pRemoteCharacteristic == nullptr) { - ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); - return; - } - - // Read the value of the characteristic. - std::string value = pRemoteCharacteristic->readValue(); - ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); - - while(1) { - // Set a new value of the characteristic - ESP_LOGD(LOG_TAG, "Setting the new value"); - std::ostringstream stringStream; - struct timeval tv; - gettimeofday(&tv, nullptr); - stringStream << "Time since boot: " << tv.tv_sec; - pRemoteCharacteristic->writeValue(stringStream.str()); - - FreeRTOS::sleep(1000); - } - - pClient->disconnect(); - - ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); - ESP_LOGD(LOG_TAG, "-- End of task"); - } // run -}; // MyClient - - -/** - * Scan for BLE servers and find the first one that advertises the service we are looking for. - */ -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - void onResult(BLEAdvertisedDevice advertisedDevice) { - ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); - - if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { - advertisedDevice.getScan()->stop(); - - ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); - MyClient* pMyClient = new MyClient(); - pMyClient->setStackSize(18000); - pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); - } // Found our server - } // onResult -}; // MyAdvertisedDeviceCallbacks - - -/** - * Perform the work of a sample BLE client. - */ -void SampleSecureClient(void) { - ESP_LOGD(LOG_TAG, "Scanning sample starting"); - BLEDevice::init(""); - BLEScan *pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setActiveScan(true); - pBLEScan->start(15); -} // SampleClient diff --git a/cpp_utils/tests/BLETests/SampleSecureServer.cpp b/cpp_utils/tests/BLETests/SampleSecureServer.cpp deleted file mode 100644 index 97c9037f..00000000 --- a/cpp_utils/tests/BLETests/SampleSecureServer.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Create a new BLE server. - */ -#include "BLEDevice.h" -#include "BLEServer.h" -#include "BLEUtils.h" -#include "BLE2902.h" -#include -#include -#include - - -#include "sdkconfig.h" - -static char LOG_TAG[] = "SampleServer"; - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onSecurityRequest(){ - return true; - } - void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ - if(auth_cmpl.success){ - ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); - esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); - ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); - } - ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); - } -}; - -class MainBLEServer: public Task { - void run(void *data) { - ESP_LOGD(LOG_TAG, "Starting BLE work!"); - - BLEDevice::init("ESP32"); - BLEServer* pServer = BLEDevice::createServer(); - pServer->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - pServer->setSecurityCallbacks(new MySecurity()); - - BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); - - BLECharacteristic* pCharacteristic = pService->createCharacteristic( - BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), - BLECharacteristic::PROPERTY_BROADCAST | BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_INDICATE - ); - pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - - pCharacteristic->setValue("Hello World!"); - - BLE2902* p2902Descriptor = new BLE2902(); - p2902Descriptor->setNotifications(true); - pCharacteristic->addDescriptor(p2902Descriptor); - - pService->start(); - - BLEAdvertising* pAdvertising = pServer->getAdvertising(); - pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); - - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); - pSecurity->setCapability(ESP_IO_CAP_NONE); - pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - - pAdvertising->start(); - - ESP_LOGD(LOG_TAG, "Advertising started!"); - delay(1000000); - } -}; - - -void SampleSecureServer(void) -{ - //esp_log_level_set("*", ESP_LOG_DEBUG); - MainBLEServer* pMainBleServer = new MainBLEServer(); - pMainBleServer->setStackSize(20000); - pMainBleServer->start(); - -} // app_main From 2fbca3c6b6fbe229a6dfaf348a1aaab1636c2c1d Mon Sep 17 00:00:00 2001 From: anio Date: Thu, 28 Dec 2017 14:36:20 +0200 Subject: [PATCH 140/310] Add timeout related to https://github.com/nkolban/esp32-snippets/issues/322 --- cpp_utils/HttpServer.cpp | 18 ++++++++++++++++++ cpp_utils/HttpServer.h | 3 +++ cpp_utils/Socket.cpp | 30 ++++++++++++++++++++++++++++-- cpp_utils/Socket.h | 2 ++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 488f05ce..3d68650b 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -28,6 +28,7 @@ static const char* LOG_TAG = "HttpServer"; HttpServer::HttpServer() { m_fileBufferSize = 4*1024; // Default size of the file buffer. m_portNumber = 80; // The default port number. + m_clientTimeout = 5; // The default timeout 5 seconds. m_rootPath = ""; // The default path. m_useSSL = false; // Default SSL is no. setDirectoryListing(false); // Default directory listing is disabled. @@ -159,6 +160,7 @@ class HttpServerTask: public Task { //Memory::checkIntegrity(); try { clientSocket = m_pHttpServer->m_socket.accept(); // Block waiting for a new external client connection. + clientSocket.setTimeout(m_pHttpServer->getClientTimeout()); } catch(std::exception &e) { ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); @@ -329,6 +331,22 @@ void HttpServer::listDirectory(std::string path, HttpResponse& response) { response.close(); } // listDirectory +/** + * @brief Set different socket timeout for new connections. + * @param [in] use Set to true to enable directory listing. + */ +void HttpServer::setClientTimeout(uint32_t timeout) { + m_clientTimeout = timeout; +} + +/** + * @brief Get current socket's timeout for new connections. + * @param [in] use Set to true to enable directory listing. + */ +uint32_t HttpServer::getClientTimeout() { + return m_clientTimeout; +} + /** * @brief Set whether or not we will list directories. * @param [in] use Set to true to enable directory listing. diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index c814d0c8..2d92d76f 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -81,6 +81,8 @@ class HttpServer { void setDirectoryListing(bool use); // Should we list the content of directories? void setFileBufferSize(size_t fileBufferSize); // Set the size of the file buffer void setRootPath(std::string path); // Set the root of the file system path. + void setClientTimeout(uint32_t timeout); // Set client's socket timeout + uint32_t getClientTimeout(); // Get client's socket timeout void start(uint16_t portNumber, bool useSSL=false); void stop(); // Stop a previously started server. @@ -95,6 +97,7 @@ class HttpServer { std::string m_rootPath; // Root path into the file system. Socket m_socket; bool m_useSSL; // Is this server listening on an HTTPS port? + uint32_t m_clientTimeout; // Default Timeout }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index b7c41c1e..8f350094 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -60,8 +60,10 @@ Socket Socket::accept() { struct sockaddr addr; getBind(&addr); ESP_LOGD(LOG_TAG, ">> accept: Accepting on %s; sockFd: %d, using SSL: %d", addressToString(&addr).c_str(), m_sock, getSSL()); - - int clientSockFD = ::lwip_accept_r(m_sock, nullptr, nullptr); + struct sockaddr_in client_addr; + socklen_t sin_size; + int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr *)&client_addr, &sin_size); + printf("------> new connection client %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (clientSockFD == -1) { SocketException se(errno); ESP_LOGE(LOG_TAG, "accept(): %s, m_sock=%d", strerror(errno), m_sock); @@ -272,6 +274,30 @@ bool Socket::operator <(const Socket& other) const { return m_sock < other.m_sock; } +int Socket::setSocketOption(int option, char* value, size_t len) +{ + int res = setsockopt(m_sock, SOL_SOCKET, option, value, len); + if(res < 0) { + ESP_LOGE(LOG_TAG, "%X : %d", option, errno); + } + return res; +} + +/** + * @brief Socket timeout. + * @param [in] seconds to wait. + */ +int Socket::setTimeout(uint32_t seconds) +{ + struct timeval tv; + tv.tv_sec = seconds; + tv.tv_usec = 0; + if(setSocketOption(SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) { + return -1; + } + return setSocketOption(SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); +} + std::string Socket::readToDelim(std::string delim) { std::string ret; diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 6eef11db..a4a71398 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -68,6 +68,8 @@ class Socket { int connect(struct in_addr address, uint16_t port); int connect(char* address, uint16_t port); int createSocket(bool isDatagram = false); + int setSocketOption(int option, char* value, size_t len); + int setTimeout(uint32_t seconds); void getBind(struct sockaddr* pAddr); int getFD() const; bool getSSL() const; From 744af7b2e27d2f63eee6a591eddee141d39b70aa Mon Sep 17 00:00:00 2001 From: anio Date: Fri, 29 Dec 2017 17:31:40 +0200 Subject: [PATCH 141/310] Disable timeout for WebSocket's #322 --- cpp_utils/HttpServer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 3d68650b..c6f4b419 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -171,6 +171,9 @@ class HttpServerTask: public Task { HttpRequest request(clientSocket); // Build the HTTP Request from the socket. + if (request.isWebsocket()) { // If this is a WebSocket + clientSocket.setTimeout(0); // Clear the timeout. + } request.dump(); // debug. processRequest(request); // Process the request. if (!request.isWebsocket()) { // If this is NOT a WebSocket, then close it as the request From 20214be69235b5e7da3321d599282b64e37b9028 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 30 Dec 2017 12:31:53 -0600 Subject: [PATCH 142/310] Fixes for #328 --- cpp_utils/BLECharacteristic.cpp | 23 ++++++++++++++++------- cpp_utils/HttpServer.cpp | 10 +++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 52310d7a..2e1a1b6a 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -327,17 +327,16 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_READ_EVT: { if (param->read.handle == m_handle) { - if (m_pCallbacks != nullptr) { - m_pCallbacks->onRead(this); // Invoke the read callback. - } + // Here's an interesting thing. The read request has the option of saying whether we need a response // or not. What would it "mean" to receive a read request and NOT send a response back? That feels like // a very strange read. // // We have to handle the case where the data we wish to send back to the client is greater than the maximum -// packet size of 22 bytes. In this case, we become responsible for chunking the data into uints of 22 bytes. -// The apparent algorithm is as follows. +// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes. +// The apparent algorithm is as follows: +// // If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. // If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than // 22 bytes, then we "just" send it and thats the end of the story. @@ -359,8 +358,10 @@ void BLECharacteristic::handleGATTServerEvent( if (param->read.need_rsp) { ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; - std::string value = m_value.getValue(); + if (param->read.is_long) { + std::string value = m_value.getValue(); + if (value.length() - m_value.getReadOffset() < maxOffset) { // This is the last in the chain rsp.attr_value.len = value.length() - m_value.getReadOffset(); @@ -374,7 +375,14 @@ void BLECharacteristic::handleGATTServerEvent( memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); m_value.setReadOffset(rsp.attr_value.offset + maxOffset); } - } else { + } else { // read.is_long == false + + if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + m_pCallbacks->onRead(this); // Invoke the read callback. + } + + std::string value = m_value.getValue(); + if (value.length()+1 > maxOffset) { // Too big for a single shot entry. m_value.setReadOffset(maxOffset); @@ -408,6 +416,7 @@ void BLECharacteristic::handleGATTServerEvent( break; } // ESP_GATTS_READ_EVT + // ESP_GATTS_CONF_EVT // // conf: diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index c6f4b419..fe31af83 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -1,5 +1,10 @@ /* * HttpServer.cpp + * Design: + * This class represents an HTTP server. We create an instance of the class and then it is configured. + * When the user has completed the configuration, they execute the start() method which starts it running. + * A subsequent call to stop() will stop it. When start() is called, a new task is created which listens + * for incoming messages. * * Created on: Aug 30, 2017 * Author: kolban @@ -157,7 +162,7 @@ class HttpServerTask: public Task { while(1) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); - //Memory::checkIntegrity(); + try { clientSocket = m_pHttpServer->m_socket.accept(); // Block waiting for a new external client connection. clientSocket.setTimeout(m_pHttpServer->getClientTimeout()); @@ -167,8 +172,7 @@ class HttpServerTask: public Task { return; } - ESP_LOGD("HttpServerTask", "HttpServer listening on port %d received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); - + ESP_LOGD("HttpServerTask", "HttpServer listening on port %d has received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); HttpRequest request(clientSocket); // Build the HTTP Request from the socket. if (request.isWebsocket()) { // If this is a WebSocket From 3943f99a5dba851e6e62be0ed753619361ed79a0 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 30 Dec 2017 19:20:22 -0600 Subject: [PATCH 143/310] Code changes for #329 Note that this is an interface breaking change. --- cpp_utils/WebSocket.cpp | 13 ++++++++----- cpp_utils/WebSocket.h | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index 791be38c..14642c34 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -158,7 +158,7 @@ class WebSocketReader: public Task { case OPCODE_BINARY: { if (pWebSocketHandler != nullptr) { WebSocketInputStreambuf streambuf(pWebSocket->getSocket(), payloadLen, frame.mask==1?mask:nullptr); - pWebSocketHandler->onMessage(&streambuf); + pWebSocketHandler->onMessage(&streambuf, pWebSocket); //streambuf.discard(); } break; @@ -203,7 +203,8 @@ class WebSocketReader: public Task { * If no over-riding handler is provided for the "close" event, this method is called. */ void WebSocketHandler::onClose() { - ESP_LOGD("WebSocketHandler", ">> WebSocketHandler:onClose()"); + ESP_LOGD("WebSocketHandler", ">> onClose"); + ESP_LOGD("WebSocketHandler", "<< onClose"); } // onClose @@ -217,8 +218,9 @@ void WebSocketHandler::onClose() { * ``` * This will read the whole message into the string stream. */ -void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf) { - ESP_LOGD("WebSocketHandler", ">> onMessage") +void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf, WebSocket *pWebSocket) { + ESP_LOGD("WebSocketHandler", ">> onMessage"); + ESP_LOGD("WebSocketHandler", "<< onMessage"); } // onData @@ -227,7 +229,8 @@ void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreamb * If no over-riding handler is provided for the "error" event, this method is called. */ void WebSocketHandler::onError(std::string error) { - ESP_LOGD("WebSocketHandler", ">> WebSocketHandler:onError()"); + ESP_LOGD("WebSocketHandler", ">> onError: %s", error.c_str()); + ESP_LOGD("WebSocketHandler", "<< onError"); } // onError diff --git a/cpp_utils/WebSocket.h b/cpp_utils/WebSocket.h index d1308f15..80c72f0c 100644 --- a/cpp_utils/WebSocket.h +++ b/cpp_utils/WebSocket.h @@ -13,6 +13,7 @@ #undef close #undef send class WebSocketReader; +class WebSocket; // +-------------------------------+ // | WebSocketInputStreambuf | @@ -45,7 +46,7 @@ class WebSocketHandler { public: virtual ~WebSocketHandler(); virtual void onClose(); - virtual void onMessage(WebSocketInputStreambuf *pWebSocketInputStreambuf); + virtual void onMessage(WebSocketInputStreambuf *pWebSocketInputStreambuf, WebSocket *pWebSocket); virtual void onError(std::string error); }; From 5afeced69002e6ffffd7cf1676ad11891217de3f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 31 Dec 2017 11:05:56 -0600 Subject: [PATCH 144/310] Fixes for #332 --- cpp_utils/JSON.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 1f752c63..2c1de785 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -229,10 +229,7 @@ JsonArray JsonObject::getArray(std::string name) { */ bool JsonObject::getBoolean(std::string name) { cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node->valueint == 0) { - return false; - } - return true; + return cJSON_IsTrue(node); } // getBoolean @@ -316,7 +313,7 @@ void JsonObject::setArray(std::string name, JsonArray array) { * @return N/A. */ void JsonObject::setBoolean(std::string name, bool value) { - cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateBool(value)); + cJSON_AddItemToObject(m_node, name.c_str(), value?cJSON_CreateTrue():cJSON_CreateFalse()); } // setBoolean From 82ba6d77b7b156685dd300297c240c13db8ff3bb Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 1 Jan 2018 09:17:57 -0600 Subject: [PATCH 145/310] Fixes for #335 --- cpp_utils/BLEDevice.cpp | 5 ++- cpp_utils/BLEHIDDevice.cpp | 6 +++ cpp_utils/BLEHIDDevice.h | 6 ++- cpp_utils/BLESecurity.cpp | 90 +++++++++++++++++++++----------------- cpp_utils/BLESecurity.h | 22 +++++++--- cpp_utils/Socket.cpp | 2 +- 6 files changed, 80 insertions(+), 51 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index a2dd80b4..427666c1 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -142,7 +142,10 @@ uint16_t BLEDevice::m_localMTU = 23; switch(event) { case ESP_GATTC_CONNECT_EVT: { if(BLEDevice::getMTU() != 23){ - esp_err_t err = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); + esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } } if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index c5485a81..3e7fbc89 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -4,6 +4,10 @@ * Created on: Dec 18, 2017 * Author: chegewara */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + //#include "BLEUUID.h" #include "BLEHIDDevice.h" @@ -152,3 +156,5 @@ BLEDescriptor* BLEHIDDevice::featureReport() { BLEDescriptor* BLEHIDDevice::batteryLevel() { return m_batteryLevelDescriptor; } + +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEHIDDevice.h b/cpp_utils/BLEHIDDevice.h index 43e4ccdc..bf2b29e0 100644 --- a/cpp_utils/BLEHIDDevice.h +++ b/cpp_utils/BLEHIDDevice.h @@ -7,6 +7,10 @@ #ifndef _BLEHIDDEVICE_H_ #define _BLEHIDDEVICE_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + #include "BLECharacteristic.h" #include "BLEService.h" #include "BLEDescriptor.h" @@ -83,5 +87,5 @@ class BLEHIDDevice { BLE2902* m_batteryLevelNotifications; //0x2902 }; - +#endif // CONFIG_BT_ENABLED #endif /* _BLEHIDDEVICE_H_ */ diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp index 273ec77f..4cf964a7 100644 --- a/cpp_utils/BLESecurity.cpp +++ b/cpp_utils/BLESecurity.cpp @@ -6,6 +6,8 @@ */ #include +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) BLESecurity::BLESecurity() { } @@ -19,79 +21,85 @@ void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { m_authReq = auth_req; esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode } -/* + +/** * @brief Set our device IO capability to let end user perform authorization * either by displaying or entering generated 6-digits pin code */ void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { m_iocap = iocap; esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); -} -/* +} // setCapability + + +/** * @brief Init encryption key by server * @param key_size is value between 7 and 16 */ void BLESecurity::setInitEncryptionKey(uint8_t init_key) { m_initKey = init_key; esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); -} +} // setInitEncryptionKey -/* + +/** * @brief Init encryption key by client * @param key_size is value between 7 and 16 */ void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { m_respKey = resp_key; esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); -} +} // setRespEncryptionKey -/* + +/** * * */ void BLESecurity::setKeySize(uint8_t key_size) { m_keySize = key_size; esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); -} +} //setKeySize -/* + +/** * @brief Debug function to display what keys are exchanged by peers */ char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { - char *key_str = NULL; - switch(key_type) { - case ESP_LE_KEY_NONE: - key_str = (char*)"ESP_LE_KEY_NONE"; - break; - case ESP_LE_KEY_PENC: - key_str = (char*)"ESP_LE_KEY_PENC"; - break; - case ESP_LE_KEY_PID: - key_str = (char*)"ESP_LE_KEY_PID"; - break; - case ESP_LE_KEY_PCSRK: - key_str = (char*)"ESP_LE_KEY_PCSRK"; - break; - case ESP_LE_KEY_PLK: - key_str = (char*)"ESP_LE_KEY_PLK"; - break; - case ESP_LE_KEY_LLK: - key_str = (char*)"ESP_LE_KEY_LLK"; - break; + char* key_str = nullptr; + switch(key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*)"ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*)"ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*)"ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*)"ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*)"ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*)"ESP_LE_KEY_LLK"; + break; case ESP_LE_KEY_LENC: - key_str = (char*)"ESP_LE_KEY_LENC"; - break; + key_str = (char*)"ESP_LE_KEY_LENC"; + break; case ESP_LE_KEY_LID: - key_str = (char*)"ESP_LE_KEY_LID"; - break; + key_str = (char*)"ESP_LE_KEY_LID"; + break; case ESP_LE_KEY_LCSRK: - key_str = (char*)"ESP_LE_KEY_LCSRK"; - break; + key_str = (char*)"ESP_LE_KEY_LCSRK"; + break; default: - key_str = (char*)"INVALID BLE KEY TYPE"; - break; - - } - return key_str; -} + key_str = (char*)"INVALID BLE KEY TYPE"; + break; + } + return key_str; +} // esp_key_type_to_str +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index 1a649283..67c41efb 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -7,6 +7,9 @@ #ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ #define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + #include class BLESecurity { @@ -26,7 +29,8 @@ class BLESecurity { uint8_t m_initKey; uint8_t m_respKey; uint8_t m_keySize; -}; +}; // BLESecurity + /* * @brief Callbacks to handle GAP events related to authorization @@ -35,29 +39,33 @@ class BLESecurityCallbacks { public: virtual ~BLESecurityCallbacks() {}; - /* + /** * @brief Its request from peer device to input authentication pin code displayed on peer device. * It requires that our device is capable to input 6-digits code by end user * @return Return 6-digits integer value from input device */ virtual uint32_t onPassKeyRequest() = 0; - /* + + /** * @brief Provide us 6-digits code to perform authentication. * It requires that our device is capable to display this code to end user * @param */ virtual void onPassKeyNotify(uint32_t pass_key); - /* + + /** * @brief Here we can make decision if we want to let negotiate authorization with peer device or not * return Return true if we accept this peer device request */ + virtual bool onSecurityRequest(); - /* + /** * Provide us information when authentication process is completed */ virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t); virtual bool onConfirmPIN(uint32_t pin); -}; +}; // BLESecurityCallbacks -#endif /* COMPONENTS_CPP_UTILS_BLESECURITY_H_ */ +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index 8f350094..a0b668fc 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -63,7 +63,7 @@ Socket Socket::accept() { struct sockaddr_in client_addr; socklen_t sin_size; int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr *)&client_addr, &sin_size); - printf("------> new connection client %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + //printf("------> new connection client %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (clientSockFD == -1) { SocketException se(errno); ESP_LOGE(LOG_TAG, "accept(): %s, m_sock=%d", strerror(errno), m_sock); From ac27ff1e7fc22f87e23e3383953152fcb77a8799 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 1 Jan 2018 13:22:05 -0600 Subject: [PATCH 146/310] Updates for #337 --- Documentation/BLE C++ Guide.pdf | Bin 283509 -> 286129 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Documentation/BLE C++ Guide.pdf diff --git a/Documentation/BLE C++ Guide.pdf b/Documentation/BLE C++ Guide.pdf old mode 100644 new mode 100755 index e68e0398173ea68d4dc5817a7e3f16b01ef18740..cc4ca4bbfeace1c3fbb35883e33efdba230f6b9d GIT binary patch delta 77268 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%0AP&}BM!AQ4DJZsF>Z2FIQe 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 delta 74878 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 z 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

Jo_x$JDAFV_~cU(P;gyT?Aj9I)-PD-mO0w%B&s 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 Date: Mon, 1 Jan 2018 14:52:12 -0600 Subject: [PATCH 147/310] Fixes for #331 --- cpp_utils/FreeRTOS.cpp | 41 ++++++++++++++++++++++++++++++++-------- cpp_utils/FreeRTOS.h | 4 ++-- cpp_utils/HttpServer.cpp | 15 +++++++++++++-- cpp_utils/HttpServer.h | 9 +++++---- 4 files changed, 53 insertions(+), 16 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 674e6f8e..1ae01d73 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -168,17 +168,25 @@ void FreeRTOS::Semaphore::giveFromISR() { /** * @brief Take a semaphore. * Take a semaphore and wait indefinitely. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. */ -void FreeRTOS::Semaphore::take(std::string owner) +bool FreeRTOS::Semaphore::take(std::string owner) { ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); } else { - xSemaphoreTake(m_semaphore, portMAX_DELAY); + rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY); } m_owner = owner; - ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + if (rc) { + ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; } // Semaphore::take @@ -186,20 +194,33 @@ void FreeRTOS::Semaphore::take(std::string owner) * @brief Take a semaphore. * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. * @param [in] timeoutMs Timeout in milliseconds. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. */ -void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { +bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); - + bool rc = false; if (m_usePthreads) { - assert(false); + assert(false); // We apparently don't have a timed wait for pthreads. } else { - xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); } m_owner = owner; - ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + if (rc) { + ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; } // Semaphore::take + + +/** + * @brief Create a string representation of the semaphore. + * @return A string representation of the semaphore. + */ std::string FreeRTOS::Semaphore::toString() { std::stringstream stringStream; stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; @@ -207,6 +228,10 @@ std::string FreeRTOS::Semaphore::toString() { } // toString +/** + * @brief Set the name of the semaphore. + * @param [in] name The name of the semaphore. + */ void FreeRTOS::Semaphore::setName(std::string name) { m_name = name; } // setName diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index 43a3b8f4..ab0e83d8 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -36,8 +36,8 @@ class FreeRTOS { void give(uint32_t value); void giveFromISR(); void setName(std::string name); - void take(std::string owner=""); - void take(uint32_t timeoutMs, std::string owner=""); + bool take(std::string owner=""); + bool take(uint32_t timeoutMs, std::string owner=""); std::string toString(); uint32_t wait(std::string owner=""); diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index fe31af83..15a78248 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -169,10 +169,11 @@ class HttpServerTask: public Task { } catch(std::exception &e) { ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); + m_pHttpServer->m_semaphoreServerStarted.give(); // Release the semaphore .. we are now no longer running. return; } - ESP_LOGD("HttpServerTask", "HttpServer listening on port %d has received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); + ESP_LOGD("HttpServerTask", "HttpServer that was listening on port %d has received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); HttpRequest request(clientSocket); // Build the HTTP Request from the socket. if (request.isWebsocket()) { // If this is a WebSocket @@ -414,11 +415,20 @@ void HttpServer::start(uint16_t portNumber, bool useSSL) { // Design: // The start of the HTTP server should be as fast as possible. ESP_LOGD(LOG_TAG, ">> start: port: %d, useSSL: %d", portNumber, useSSL); + + // Take the semaphore that says that we are now running. If we are already running, then end here as + // there is nothing further to do. + if (m_semaphoreServerStarted.take(100, "start") == false) { + ESP_LOGD(LOG_TAG, "<< start: Already running"); + return; + } + m_useSSL = useSSL; m_portNumber = portNumber; HttpServerTask* pHttpServerTask = new HttpServerTask("HttpServerTask"); pHttpServerTask->start(this); + ESP_LOGD(LOG_TAG, "<< start"); } // start @@ -430,7 +440,8 @@ void HttpServer::stop() { // that is listening for incoming connections. That will then shutdown all the other // activities. ESP_LOGD(LOG_TAG, ">> stop"); - m_socket.close(); + m_socket.close(); // Close the socket that is being used to watch for incoming requests. + m_semaphoreServerStarted.wait("stop"); // Wait for the server to stop. ESP_LOGD(LOG_TAG, "<< stop"); } // stop diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index 2d92d76f..95558c05 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -16,7 +16,7 @@ #include "SockServ.h" #include "HttpRequest.h" #include "HttpResponse.h" -//#include "SockServ.h" +#include "FreeRTOS.h" #include class HttpServerTask; @@ -74,15 +74,15 @@ class HttpServer { HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ); + uint32_t getClientTimeout(); // Get client's socket timeout size_t getFileBufferSize(); // Get the current size of the file buffer. uint16_t getPort(); // Get the port on which the Http server is listening. std::string getRootPath(); // Get the root of the file system path. bool getSSL(); // Are we using SSL? + void setClientTimeout(uint32_t timeout); // Set client's socket timeout void setDirectoryListing(bool use); // Should we list the content of directories? void setFileBufferSize(size_t fileBufferSize); // Set the size of the file buffer void setRootPath(std::string path); // Set the root of the file system path. - void setClientTimeout(uint32_t timeout); // Set client's socket timeout - uint32_t getClientTimeout(); // Get client's socket timeout void start(uint16_t portNumber, bool useSSL=false); void stop(); // Stop a previously started server. @@ -97,7 +97,8 @@ class HttpServer { std::string m_rootPath; // Root path into the file system. Socket m_socket; bool m_useSSL; // Is this server listening on an HTTPS port? - uint32_t m_clientTimeout; // Default Timeout + uint32_t m_clientTimeout; // Default Timeout + FreeRTOS::Semaphore m_semaphoreServerStarted = FreeRTOS::Semaphore("ServerStarted"); }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ From 1ce09382cdb0a5f3d9e7ffb83d5e8699bd95402f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 1 Jan 2018 15:19:02 -0600 Subject: [PATCH 148/310] Changes for #337 --- cpp_utils/BLEAdvertising.cpp | 60 +++++++++++++++++++++++++++------ cpp_utils/BLEAdvertising.h | 1 + cpp_utils/DesignNotes/BLECPP.md | 4 +++ 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index e026cbec..3f22f119 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -259,7 +259,7 @@ void BLEAdvertisementData::addData(std::string data) { void BLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; + cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); } // setAppearance @@ -274,7 +274,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); break; } @@ -282,7 +282,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { case 32: { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; - cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); break; } @@ -290,7 +290,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { case 128: { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; - cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); break; } @@ -315,7 +315,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { void BLEAdvertisementData::setFlags(uint8_t flag) { char cdata[3]; cdata[0] = 2; - cdata[1] = ESP_BLE_AD_TYPE_FLAG; + cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 cdata[2] = flag; addData(std::string(cdata, 3)); } // setFlag @@ -330,7 +330,7 @@ void BLEAdvertisementData::setManufacturerData(std::string data) { ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); char cdata[2]; cdata[0] = data.length() + 1; - cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; + cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff addData(std::string(cdata, 2) + data); ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData @@ -344,7 +344,7 @@ void BLEAdvertisementData::setName(std::string name) { ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; - cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; + cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setName"); } // setName @@ -360,7 +360,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); break; } @@ -368,7 +368,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { case 32: { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; - cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); break; } @@ -376,7 +376,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { case 128: { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; - cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); break; } @@ -387,6 +387,44 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { } // setPartialServices +/** + * @brief Set the service data (UUID + data) + * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. + * @param [in] data The data to be associated with the service data advert. + */ +void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { + char cdata[2]; + switch(uuid.bitSize()) { + case 16: { + // [Len] [0x16] [UUID16] data + cdata[0] = data.length() + 3; + cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data); + break; + } + + case 32: { + // [Len] [0x20] [UUID32] data + cdata[0] = data.length() + 5; + cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data); + break; + } + + case 128: { + // [Len] [0x21] [UUID128] data + cdata[0] = data.length() + 17; + cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data); + break; + } + + default: + return; + } +} // setServiceData + + /** * @brief Set the short name. * @param [in] The short name of the device. @@ -395,7 +433,7 @@ void BLEAdvertisementData::setShortName(std::string name) { ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; - cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; + cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setShortName"); } // setShortName diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 8d53b64e..c9d5ba98 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -56,6 +56,7 @@ class BLEAdvertisementData { void setManufacturerData(std::string data); void setName(std::string name); void setPartialServices(BLEUUID uuid); + void setServiceData(BLEUUID uuid, std::string data); void setShortName(std::string name); private: diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md index 9ce9715a..64e7a04c 100644 --- a/cpp_utils/DesignNotes/BLECPP.md +++ b/cpp_utils/DesignNotes/BLECPP.md @@ -40,7 +40,10 @@ The following advertising types are supported: |0x07|Complete list of 128 bit service UUIDs |0x08|Shortened local name |0x09|Complete local name +|0x16|Service data (16 bit) |0x19|Appearance +|0x20|Service data (32 bit) +|0x21|Service data (128 bit) |0xFF|Manufacturer data @@ -61,5 +64,6 @@ See also: |0x03, 0x05, 0x07|`setCompleteServices(BLEUUID)` |0x08|`setShortName(std::string)` |0x09|`setName(std::string)` +|0x16, 0x20, 0x21|`setServiceData(BLEUUID, std::string)` |0x19|`setAppearance(uint16_t)` |0xFF|`setManufacturerData(std::string)` \ No newline at end of file From 59f667e5614c611ad68c8d1766b731e911de43bf Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 3 Jan 2018 19:31:29 -0600 Subject: [PATCH 149/310] Changes for #333 --- cpp_utils/Socket.cpp | 35 +++++++++++++++++++++++------------ cpp_utils/Socket.h | 6 +++--- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index a0b668fc..602bdc74 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -107,9 +107,9 @@ std::string Socket::addressToString(struct sockaddr* addr) { * Specify an address of INADDR_ANY to use the local server IP. * @param [in] port Port number to bind. * @param [in] address Address to bind. - * @return N/A + * @return Returns 0 on success. */ -void Socket::bind(uint16_t port, uint32_t address) { +int Socket::bind(uint16_t port, uint32_t address) { ESP_LOGD(LOG_TAG, ">> bind: port=%d, address=0x%x", port, address); if (m_sock == -1) { @@ -120,35 +120,39 @@ void Socket::bind(uint16_t port, uint32_t address) { serverAddress.sin_addr.s_addr = htonl(address); serverAddress.sin_port = htons(port); int rc = ::lwip_bind_r(m_sock, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); - if (rc == -1) { + if (rc != 0) { ESP_LOGE(LOG_TAG, "<< bind: bind[socket=%d]: %d: %s", m_sock, errno, strerror(errno)); - return; + return rc; } ESP_LOGD(LOG_TAG, "<< bind"); + return rc; } // bind /** * @brief Close the socket. * - * @return N/A. + * @return Returns 0 on success. */ -void Socket::close() { +int Socket::close() { ESP_LOGD(LOG_TAG, "close: m_sock=%d, ssl: %d", m_sock, getSSL()); + int rc; if (getSSL()) { - int rc = mbedtls_ssl_close_notify(&m_sslContext); + rc = mbedtls_ssl_close_notify(&m_sslContext); if (rc < 0) { ESP_LOGD(LOG_TAG, "mbedtls_ssl_close_notify: %d", rc); } } + rc = 0; if (m_sock != -1) { ESP_LOGD(LOG_TAG, "Calling lwip_close on %d", m_sock); - int rc = lwip_close_r(m_sock); + rc = ::lwip_close_r(m_sock); if (rc != 0) { - ESP_LOGE(LOG_TAG, "Error with lwip_close"); + ESP_LOGE(LOG_TAG, "Error with lwip_close: %d", rc); } } m_sock = -1; + return rc; } // close @@ -253,20 +257,27 @@ bool Socket::isValid() { * @brief Create a listening socket. * @param [in] port The port number to listen upon. * @param [in] isDatagram True if we are listening on a datagram. The default is false. + * @return Returns 0 on success. */ -void Socket::listen(uint16_t port, bool isDatagram) { +int Socket::listen(uint16_t port, bool isDatagram) { ESP_LOGD(LOG_TAG, ">> listen: port: %d, isDatagram: %d", port, isDatagram); createSocket(isDatagram); - bind(port, 0); + int rc = bind(port, 0); + if (rc != 0) { + ESP_LOGE(LOG_TAG, "<< listen: Error in bind: %s", strerror(errno)); + return rc; + } // For a datagram socket, we don't execute a listen call. That is is only for connection oriented // sockets. if (!isDatagram) { - int rc = ::lwip_listen_r(m_sock, 5); + rc = ::lwip_listen_r(m_sock, 5); if (rc == -1) { ESP_LOGE(LOG_TAG, "<< listen: %s", strerror(errno)); + return rc; } } ESP_LOGD(LOG_TAG, "<< listen"); + return 0; } // listen diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index a4a71398..7685bf7c 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -63,8 +63,8 @@ class Socket { Socket accept(); static std::string addressToString(struct sockaddr* addr); - void bind(uint16_t port, uint32_t address); - void close(); + int bind(uint16_t port, uint32_t address); + int close(); int connect(struct in_addr address, uint16_t port); int connect(char* address, uint16_t port); int createSocket(bool isDatagram = false); @@ -74,7 +74,7 @@ class Socket { int getFD() const; bool getSSL() const; bool isValid(); - void listen(uint16_t port, bool isDatagram=false); + int listen(uint16_t port, bool isDatagram=false); bool operator<(const Socket& other) const; std::string readToDelim(std::string delim); size_t receive(uint8_t* data, size_t length, bool exact=false); From 52cb88c6e2a046a58b9bceac0861066988469d97 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 4 Jan 2018 03:46:30 +0100 Subject: [PATCH 150/310] Fully functional HID library with keyboard example --- cpp_utils/BLEDevice.cpp | 1 + cpp_utils/BLEHIDDevice.cpp | 260 +++++++++++------- cpp_utils/BLEHIDDevice.h | 46 +--- cpp_utils/HIDTypes.h | 9 +- .../tests/BLETests/SampleHIDKeyboard.cpp | 192 +++++++++++++ 5 files changed, 382 insertions(+), 126 deletions(-) create mode 100644 cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 427666c1..b333ed7c 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -98,6 +98,7 @@ uint16_t BLEDevice::m_localMTU = 23; switch(event) { case ESP_GATTS_CONNECT_EVT: { + BLEDevice::m_localMTU = 23; if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index 3e7fbc89..29376f3a 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -1,160 +1,234 @@ /* * BLEHIDDevice.cpp * - * Created on: Dec 18, 2017 + * Created on: Jan 03, 2018 * Author: chegewara */ - #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -//#include "BLEUUID.h" #include "BLEHIDDevice.h" +#include "BLE2904.h" + BLEHIDDevice::BLEHIDDevice(BLEServer* server) { + /* + * Here we create mandatory services described in bluetooth specification + */ m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a)); m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40); - //m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); - createDescriptors(); - createCharacteristics(); -} + m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); -BLEHIDDevice::~BLEHIDDevice() { - // TODO Auto-generated destructor stub -} - -void BLEHIDDevice::setReportMap(uint8_t* map, uint16_t size) { - m_reportMapCharacteristic->setValue(map, size); -} - -void BLEHIDDevice::createDescriptors() { - m_inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); - const uint8_t desc1_val[] = {0x01}; - m_inputReportDescriptor->setValue((uint8_t*)desc1_val, 1); - m_inputReportNotifications = new BLE2902(); + /* + * Mandatory characteristic for device info service + */ + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, BLECharacteristic::PROPERTY_READ); - m_outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); - const uint8_t desc2_val[] = {0x02}; - m_outputReportDescriptor->setValue((uint8_t*)desc2_val, 1); + /* + * Mandatory characteristics for HID service + */ + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, BLECharacteristic::PROPERTY_READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, BLECharacteristic::PROPERTY_READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); - m_featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); - const uint8_t desc3_val[] = {0x03}; - m_featureReportDescriptor->setValue((uint8_t*)desc3_val, 1); + /* + * Mandatory battery level characteristic with notification and presence descriptor + */ + BLE2904* batteryLevelDescriptor = new BLE2904(); + batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8); + batteryLevelDescriptor->setNamespace(1); + batteryLevelDescriptor->setUnit(0x27ad); - m_bootInputNotifications = new BLE2902(); + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); + m_batteryLevelCharacteristic->addDescriptor(new BLE2902()); - if(m_batteryService != nullptr){ - m_batteryLevelDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2904)); - m_batteryLevelNotifications = new BLE2902(); - } + /* + * This value is setup here because its default value in most usage cases, its very rare to use boot mode + * and we want to simplify library using as much as possible + */ + const uint8_t pMode[] = {0x01}; + protocolMode()->setValue((uint8_t*)pMode, 1); } -void BLEHIDDevice::createCharacteristics() { - m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, BLECharacteristic::PROPERTY_READ); - m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, BLECharacteristic::PROPERTY_READ); +BLEHIDDevice::~BLEHIDDevice() { +} - m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, BLECharacteristic::PROPERTY_READ); - m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, BLECharacteristic::PROPERTY_READ); - m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); - m_inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); - m_outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); - m_featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); - m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR); - m_bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY); - m_bootOutputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); - - m_inputReportCharacteristic->addDescriptor(m_inputReportDescriptor); - m_inputReportCharacteristic->addDescriptor(m_inputReportNotifications); - m_outputReportCharacteristic->addDescriptor(m_outputReportDescriptor); - m_featureReportCharacteristic->addDescriptor(m_featureReportDescriptor); - m_bootInputCharacteristic->addDescriptor(m_bootInputNotifications); - if(m_batteryService != nullptr){ - m_batteryLevelCharacteristic->addDescriptor(m_batteryLevelDescriptor); //OPTIONAL? - m_batteryLevelCharacteristic->addDescriptor(m_batteryLevelNotifications); //OPTIONAL? - } +/* + * @brief + */ +void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { + m_reportMapCharacteristic->setValue(map, size); } +/* + * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service + */ void BLEHIDDevice::startServices() { m_deviceInfoService->start(); m_hidService->start(); - if(m_batteryService!=nullptr) - m_batteryService->start(); + m_batteryService->start(); } -BLEService* BLEHIDDevice::deviceInfo() { - return m_deviceInfoService; +/* + * @brief Create manufacturer characteristic (this characteristic is optional) + */ +BLECharacteristic* BLEHIDDevice::manufacturer() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, BLECharacteristic::PROPERTY_READ); + return m_manufacturerCharacteristic; } -BLEService* BLEHIDDevice::hidService() { - return m_hidService; +/* + * @brief Set manufacturer name + * @param [in] name manufacturer name + */ +void BLEHIDDevice::manufacturer(std::string name) { + m_manufacturerCharacteristic->setValue(name); } -BLEService* BLEHIDDevice::batteryService() { - return m_batteryService; +/* + * @brief + */ +void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { + uint8_t pnp[] = {sig, (uint8_t)(vid>>8), (uint8_t)vid, (uint8_t)(pid>>8), (uint8_t)pid, (uint8_t)(version>>8), (uint8_t)version}; + m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); } -BLECharacteristic* BLEHIDDevice::manufacturer() { - return m_manufacturerCharacteristic; +/* + * @brief + */ +void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { + uint8_t info[] = {0x11,0x1, country, flags}; + m_hidInfoCharacteristic->setValue(info, sizeof(info));; } -BLECharacteristic* BLEHIDDevice::pnp() { - return m_pnpCharacteristic; -} +/* + * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic + * @return pointer to new input report characteristic + */ +BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { + BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); -BLECharacteristic* BLEHIDDevice::hidInfo() { - return m_hidInfoCharacteristic; + uint8_t desc1_val[] = {reportID, 0x01}; + inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); + inputReportCharacteristic->addDescriptor(new BLE2902()); + inputReportCharacteristic->addDescriptor(inputReportDescriptor); + + return inputReportCharacteristic; } -BLECharacteristic* BLEHIDDevice::reportMap() { - return m_reportMapCharacteristic; +/* + * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic + * @return Pointer to new output report characteristic + */ +BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { + BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + + uint8_t desc1_val[] = {reportID, 0x02}; + outputReportDescriptor->setValue((uint8_t*)desc1_val, 2); + outputReportCharacteristic->addDescriptor(outputReportDescriptor); + + return outputReportCharacteristic; } -BLECharacteristic* BLEHIDDevice::hidControl() { - return m_hidControlCharacteristic; +/* + * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic + * @return Pointer to new feature report characteristic + */ +BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { + BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); + BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + + uint8_t desc1_val[] = {reportID, 0x03}; + featureReportDescriptor->setValue((uint8_t*)desc1_val, 2); + featureReportCharacteristic->addDescriptor(featureReportDescriptor); + + return featureReportCharacteristic; } -BLECharacteristic* BLEHIDDevice::inputReport(void*) { - return m_inputReportCharacteristic; +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootInput() { + BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY); + bootInputCharacteristic->addDescriptor(new BLE2902()); + + return bootInputCharacteristic; } -BLECharacteristic* BLEHIDDevice::outputReport(void*) { - return m_outputReportCharacteristic; +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootOutput() { + return m_hidService->createCharacteristic((uint16_t)0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); } -BLECharacteristic* BLEHIDDevice::featureReport(void*) { - return m_featureReportCharacteristic; +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::hidControl() { + return m_hidControlCharacteristic; } +/* + * @brief + */ BLECharacteristic* BLEHIDDevice::protocolMode() { return m_protocolModeCharacteristic; } -BLECharacteristic* BLEHIDDevice::bootInput() { - return m_bootInputCharacteristic; +void BLEHIDDevice::setBatteryLevel(uint8_t level) { + m_batteryLevelCharacteristic->setValue(&level, 1); } - -BLECharacteristic* BLEHIDDevice::bootOutput() { - return m_bootOutputCharacteristic; +/* + * @brief Returns battery level characteristic + * @ return battery level characteristic + *//* +BLECharacteristic* BLEHIDDevice::batteryLevel() { + return m_batteryLevelCharacteristic; } -BLECharacteristic* BLEHIDDevice::batteryLevel(void*) { - return m_batteryLevelCharacteristic; + + +BLECharacteristic* BLEHIDDevice::reportMap() { + return m_reportMapCharacteristic; } -BLEDescriptor* BLEHIDDevice::inputReport() { - return m_inputReportDescriptor; +BLECharacteristic* BLEHIDDevice::pnp() { + return m_pnpCharacteristic; } -BLEDescriptor* BLEHIDDevice::outputReport() { - return m_outputReportDescriptor; + +BLECharacteristic* BLEHIDDevice::hidInfo() { + return m_hidInfoCharacteristic; +} +*/ +/* + * @brief + */ +BLEService* BLEHIDDevice::deviceInfo() { + return m_deviceInfoService; } -BLEDescriptor* BLEHIDDevice::featureReport() { - return m_featureReportDescriptor; +/* + * @brief + */ +BLEService* BLEHIDDevice::hidService() { + return m_hidService; } -BLEDescriptor* BLEHIDDevice::batteryLevel() { - return m_batteryLevelDescriptor; +/* + * @brief + */ +BLEService* BLEHIDDevice::batteryService() { + return m_batteryService; } #endif // CONFIG_BT_ENABLED + diff --git a/cpp_utils/BLEHIDDevice.h b/cpp_utils/BLEHIDDevice.h index bf2b29e0..319fd42a 100644 --- a/cpp_utils/BLEHIDDevice.h +++ b/cpp_utils/BLEHIDDevice.h @@ -1,7 +1,7 @@ /* * BLEHIDDevice.h * - * Created on: Dec 18, 2017 + * Created on: Jan 03, 2018 * Author: chegewara */ @@ -32,7 +32,7 @@ class BLEHIDDevice { BLEHIDDevice(BLEServer*); virtual ~BLEHIDDevice(); - void setReportMap(uint8_t* map, uint16_t); + void reportMap(uint8_t* map, uint16_t); void startServices(); BLEService* deviceInfo(); @@ -40,27 +40,25 @@ class BLEHIDDevice { BLEService* batteryService(); BLECharacteristic* manufacturer(); - BLECharacteristic* pnp(); - BLECharacteristic* hidInfo(); - BLECharacteristic* reportMap(); + void manufacturer(std::string name); + //BLECharacteristic* pnp(); + void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); + //BLECharacteristic* hidInfo(); + void hidInfo(uint8_t country, uint8_t flags); + //BLECharacteristic* batteryLevel(); + void setBatteryLevel(uint8_t level); + + + //BLECharacteristic* reportMap(); BLECharacteristic* hidControl(); - BLECharacteristic* inputReport(void*); - BLECharacteristic* outputReport(void*); - BLECharacteristic* featureReport(void*); + BLECharacteristic* inputReport(uint8_t reportID); + BLECharacteristic* outputReport(uint8_t reportID); + BLECharacteristic* featureReport(uint8_t reportID); BLECharacteristic* protocolMode(); BLECharacteristic* bootInput(); BLECharacteristic* bootOutput(); - BLECharacteristic* batteryLevel(void*); - - BLEDescriptor* inputReport(); - BLEDescriptor* outputReport(); - BLEDescriptor* featureReport(); - BLEDescriptor* batteryLevel(); private: - void createCharacteristics(); - void createDescriptors(); - BLEService* m_deviceInfoService; //0x180a BLEService* m_hidService; //0x1812 BLEService* m_batteryService = 0; //0x180f @@ -70,22 +68,8 @@ class BLEHIDDevice { BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a BLECharacteristic* m_reportMapCharacteristic; //0x2a4b BLECharacteristic* m_hidControlCharacteristic; //0x2a4c - BLECharacteristic* m_inputReportCharacteristic; //0x2a4d - BLECharacteristic* m_outputReportCharacteristic; //0x2a4d - BLECharacteristic* m_featureReportCharacteristic; //0x2a4d BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e - BLECharacteristic* m_bootInputCharacteristic; //0x2a22 - BLECharacteristic* m_bootOutputCharacteristic; //0x2a32 BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 - - BLEDescriptor* m_inputReportDescriptor; //0x2908 - BLEDescriptor* m_outputReportDescriptor; //0x2908 - BLEDescriptor* m_featureReportDescriptor; //0x2908 - BLE2902* m_inputReportNotifications; //0x2902 - BLE2902* m_bootInputNotifications; //0x2902 - BLEDescriptor* m_batteryLevelDescriptor; //0x2904 - BLE2902* m_batteryLevelNotifications; //0x2902 - }; #endif // CONFIG_BT_ENABLED #endif /* _BLEHIDDEVICE_H_ */ diff --git a/cpp_utils/HIDTypes.h b/cpp_utils/HIDTypes.h index b8b181be..726b84be 100644 --- a/cpp_utils/HIDTypes.h +++ b/cpp_utils/HIDTypes.h @@ -45,8 +45,13 @@ /* of data as per HID Class standard */ /* Main items */ +#ifdef ARDUINO_ARCH_ESP32 +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#else #define INPUT(size) (0x80 | size) #define OUTPUT(size) (0x90 | size) +#endif #define FEATURE(size) (0xb0 | size) #define COLLECTION(size) (0xa0 | size) #define END_COLLECTION(size) (0xc0 | size) @@ -59,9 +64,9 @@ #define PHYSICAL_MAXIMUM(size) (0x44 | size) #define UNIT_EXPONENT(size) (0x54 | size) #define UNIT(size) (0x64 | size) -#define REPORT_SIZE(size) (0x74 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits #define REPORT_ID(size) (0x84 | size) -#define REPORT_COUNT(size) (0x94 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes #define PUSH(size) (0xa4 | size) #define POP(size) (0xb4 | size) diff --git a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp new file mode 100644 index 00000000..7564cd96 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp @@ -0,0 +1,192 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#include "HIDKeyboardTypes.h" +#include +#include +#include + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleHIDDevice"; + +static BLEHIDDevice* hid; +BLECharacteristic* input; +BLECharacteristic* output; + +/* + * This callback is connect with output report. In keyboard output report report special keys changes, like CAPSLOCK, NUMLOCK + * We can add digital pins with LED to show status + * bit 1 - NUM LOCK + * bit 2 - CAPS LOCK + * bit 3 - SCROLL LOCK + */ +class MyOutputCallbacks : public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* me){ + uint8_t* value = (uint8_t*)(me->getValue().c_str()); + ESP_LOGI(LOG_TAG, "special keys: %d", *value); + } +}; + +class MyTask : public Task { + void run(void*){ + vTaskDelay(5000/portTICK_PERIOD_MS); // wait 5 seconds before send first message + const char* hello = "Hello world from esp32 hid keyboard!!!"; + while(1){ + vTaskDelay(2000/portTICK_PERIOD_MS); // simulate write message every 2 seconds + while(*hello){ + KEYMAP map = keymap[(uint8_t)*hello]; + /* + * simulate keydown, we can send up to 6 keys + */ + uint8_t a[] = {map.modifier, 0x0, map.usage, 0x0,0x0,0x0,0x0,0x0}; + input->setValue(a,sizeof(a)); + input->notify(); + + /* + * simulate keyup + */ + uint8_t v[] = {0x0, 0x0, 0x0, 0x0,0x0,0x0,0x0,0x0}; + input->setValue(v, sizeof(v)); + input->notify(); + hello++; + } + } + vTaskDelete(NULL); + } +}; + +MyTask *task; +class MyCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer){ + task->start(); + } + + void onDisconnect(BLEServer* pServer){ + task->stop(); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + task = new MyTask(); + BLEDevice::init("ESP32"); + BLEServer *pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyCallbacks()); + + /* + * Instantiate hid device + */ + hid = new BLEHIDDevice(pServer); + + + input = hid->inputReport(1); // <-- input REPORTID from report map + output = hid->outputReport(1); // <-- output REPORTID from report map + + output->setCallbacks(new MyOutputCallbacks()); + + /* + * Set manufacturer name (OPTIONAL) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.manufacturer_name_string.xml + */ + std::string name = "esp-community"; + hid->manufacturer()->setValue(name); + + /* + * Set pnp parameters (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.pnp_id.xml + */ + + hid->pnp(0x02, 0xe502, 0xa111, 0x0210); + + /* + * Set hid informations (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml + */ + hid->hidInfo(0x00,0x01); + + + /* + * Keyboard + */ + const uint8_t reportMap[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + REPORT_ID(1), 0x01, // REPORTID + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, // 1 byte (Modifier) + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x01, // 1 byte (Reserved) + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, // Num Lock + USAGE_MAXIMUM(1), 0x05, // Kana + OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x01, // 3 bits (Padding) + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x06, // 6 bytes (Keys) + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, // 101 keys + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + END_COLLECTION(0) + }; + /* + * Set report map (here is initialized device driver on client side) (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.report_map.xml + */ + hid->reportMap((uint8_t*)reportMap, sizeof(reportMap)); + + /* + * We are prepared to start hid device services. Before this point we can change all values and/or set parameters we need. + * Also before we start, if we want to provide battery info, we need to prepare battery service. + * We can setup characteristics authorization + */ + hid->startServices(); + + /* + * Its good to setup advertising by providing appearance and advertised service. This will let clients find our device by type + */ + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->setAppearance(HID_KEYBOARD); + pAdvertising->addServiceUUID(hid->hidService()->getUUID()); + pAdvertising->start(); + + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleHID(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main From 9112aebed4ef86cfccccfdbf3aedf8fe44ec08e4 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 4 Jan 2018 04:07:09 +0100 Subject: [PATCH 151/310] modified: cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp --- cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp index 7564cd96..f0385c52 100644 --- a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp +++ b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp @@ -36,9 +36,8 @@ class MyOutputCallbacks : public BLECharacteristicCallbacks { class MyTask : public Task { void run(void*){ vTaskDelay(5000/portTICK_PERIOD_MS); // wait 5 seconds before send first message - const char* hello = "Hello world from esp32 hid keyboard!!!"; while(1){ - vTaskDelay(2000/portTICK_PERIOD_MS); // simulate write message every 2 seconds + const char* hello = "Hello world from esp32 hid keyboard!!!\n"; while(*hello){ KEYMAP map = keymap[(uint8_t)*hello]; /* @@ -55,7 +54,10 @@ class MyTask : public Task { input->setValue(v, sizeof(v)); input->notify(); hello++; + + vTaskDelay(10/portTICK_PERIOD_MS); } + vTaskDelay(2000/portTICK_PERIOD_MS); // simulate write message every 2 seconds } vTaskDelete(NULL); } From dd08e7f2bf699bd4c64c9ae20034b7ac46a46008 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 3 Jan 2018 22:49:24 -0600 Subject: [PATCH 152/310] Changes for #319 --- cpp_utils/BLECharacteristic.cpp | 27 +++++++++++++++++++++------ cpp_utils/BLECharacteristic.h | 2 +- cpp_utils/BLEServer.cpp | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 2e1a1b6a..5e5aa2ac 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -199,17 +199,27 @@ std::string BLECharacteristic::getValue() { } // getValue +/** + * Handle a GATT server event. + */ void BLECharacteristic::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + switch(event) { // Events handled: + // // ESP_GATTS_ADD_CHAR_EVT - // ESP_GATTS_WRITE_EVT + // ESP_GATTS_CONF_EVT + // ESP_GATTS_CONNECT_EVT + // ESP_GATTS_DISCONNECT_EVT + // ESP_GATTS_EXEC_WRITE_EVT // ESP_GATTS_READ_EVT - // + // ESP_GATTS_WRITE_EVT + // // ESP_GATTS_EXEC_WRITE_EVT // When we receive this event it is an indication that a previous write long needs to be committed. // @@ -217,7 +227,7 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t conn_id // - uint32_t trans_id // - esp_bd_addr_t bda - // - uint8_t exec_write_flag + // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL // case ESP_GATTS_EXEC_WRITE_EVT: { if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { @@ -428,13 +438,15 @@ void BLECharacteristic::handleGATTServerEvent( break; } - case ESP_GATTS_CONNECT_EVT: + case ESP_GATTS_CONNECT_EVT: { m_semaphoreConfEvt.give(); break; + } - case ESP_GATTS_DISCONNECT_EVT: + case ESP_GATTS_DISCONNECT_EVT: { m_semaphoreConfEvt.give(); break; + } default: { break; @@ -446,7 +458,7 @@ void BLECharacteristic::handleGATTServerEvent( // event. m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); - + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); } // handleGATTServerEvent @@ -699,6 +711,7 @@ void BLECharacteristic::setWriteProperty(bool value) { } } // setWriteProperty + /** * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. @@ -717,8 +730,10 @@ std::string BLECharacteristic::toString() { return stringstream.str(); } // toString + BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} + /** * @brief Callback function to support a read request. * @param [in] pCharacteristic The characteristic that is the source of the event. diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index fefe59a0..10dc787f 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -102,7 +102,7 @@ class BLECharacteristic { BLECharacteristicCallbacks* m_pCallbacks; BLEService* m_pService; BLEValue m_value; - esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 8dd2a210..9d26eb50 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -342,6 +342,7 @@ void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect + void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); From c759f217f2ebc8ba9ae0d745a9fdfb52f9168abd Mon Sep 17 00:00:00 2001 From: anio Date: Thu, 4 Jan 2018 10:01:55 +0200 Subject: [PATCH 153/310] Move file serving to HttpResponse #342 --- cpp_utils/HttpResponse.cpp | 32 ++++++++++++++++++++++++++++++++ cpp_utils/HttpResponse.h | 1 + cpp_utils/HttpServer.cpp | 31 +++---------------------------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index adc72648..4c5c1a4e 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -5,6 +5,7 @@ * Author: kolban */ #include +#include #include "HttpRequest.h" #include "HttpResponse.h" #include @@ -123,6 +124,37 @@ void HttpResponse::sendData(uint8_t* pData, size_t size) { ESP_LOGD(LOG_TAG, "<< sendData"); } // sendData +void HttpResponse::sendFile(std::string fileName, size_t bufSize) +{ + ESP_LOGI(LOG_TAG, "Opening file: %s", fileName.c_str()); + std::ifstream ifStream; + ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. + + // If we failed to open the requested file, then it probably didn't exist so return a not found. + if (!ifStream.is_open()) { + ESP_LOGE(LOG_TAG, "Unable to open file %s for reading", fileName.c_str()); + setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found"); + addHeader(HttpRequest::HTTP_HEADER_CONTENT_TYPE, "text/plain"); + sendData("Not Found"); + close(); + return; // Since we failed to open the file, no further work to be done. + } + + // We now have an open file and want to push the content of that file through to the browser. + // because of defect #252 we have to do some pretty important re-work here. Specifically, we can't host the whole file in + // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. + + setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + uint8_t *pData = new uint8_t[bufSize]; + while(!ifStream.eof()) { + ifStream.read((char *)pData, bufSize); + sendData(pData, ifStream.gcount()); + } + delete[] pData; + ifStream.close(); + close(); +} // sendFile + /** * @brief Send the header */ diff --git a/cpp_utils/HttpResponse.h b/cpp_utils/HttpResponse.h index 9e2b4067..7080ea90 100644 --- a/cpp_utils/HttpResponse.h +++ b/cpp_utils/HttpResponse.h @@ -46,6 +46,7 @@ class HttpResponse { std::map getHeaders(); // Get all headers. void sendData(std::string data); // Send data to the client. void sendData(uint8_t* pData, size_t size); // Send data to the client. + void sendFile(std::string fileName, size_t bufSize=4*1024); // Send file contents if exists. void setStatus(int status, std::string message); // Set the response status. }; diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 15a78248..a6d16e99 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -109,41 +109,16 @@ class HttpServerTask: public Task { if (GeneralUtils::endsWith(fileName, '/')) { fileName = fileName.substr(0, fileName.length()-1); } - + + HttpResponse response(&request); // Test if the path is a directory. if (FileSystem::isDirectory(fileName)) { ESP_LOGD(LOG_TAG, "Path %s is a directory", fileName.c_str()); - HttpResponse response(&request); m_pHttpServer->listDirectory(fileName, response); // List the contents of the directory. return; } // Path was a directory. - ESP_LOGD("HttpServerTask", "Opening file: %s", fileName.c_str()); - std::ifstream ifStream; - ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. - - // If we failed to open the requested file, then it probably didn't exist so return a not found. - if (!ifStream.is_open()) { - ESP_LOGE("HttpServerTask", "Unable to open file %s for reading", fileName.c_str()); - HttpResponse response(&request); - response.setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found"); - response.sendData(""); - return; // Since we failed to open the file, no further work to be done. - } - - // We now have an open file and want to push the content of that file through to the browser. - // because of defect #252 we have to do some pretty important re-work here. Specifically, we can't host the whole file in - // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. - HttpResponse response(&request); - response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - uint8_t *pData = new uint8_t[m_pHttpServer->getFileBufferSize()]; - while(!ifStream.eof()) { - ifStream.read((char *)pData, m_pHttpServer->getFileBufferSize()); - response.sendData(pData, ifStream.gcount()); - } - delete[] pData; - ifStream.close(); - + response.sendFile(fileName, m_pHttpServer->getFileBufferSize()); } // processRequest From b603dd5507d7c404cf705a784f34f5e5d4b87a86 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 4 Jan 2018 18:22:21 -0600 Subject: [PATCH 154/310] Changes for #271 --- cpp_utils/BLEAdvertising.cpp | 37 ---------------- cpp_utils/BLEAdvertising.h | 29 ------------- cpp_utils/BLEBeacon.cpp | 84 ++++++++++++++++++++++++++++++++++++ cpp_utils/BLEBeacon.h | 43 ++++++++++++++++++ 4 files changed, 127 insertions(+), 66 deletions(-) create mode 100644 cpp_utils/BLEBeacon.cpp create mode 100644 cpp_utils/BLEBeacon.h diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 3f22f119..9e01ab78 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -449,41 +449,4 @@ std::string BLEAdvertisementData::getPayload() { } // getPayload -BLEBeacon::BLEBeacon() { - m_beaconData.manufacturerId = 0x4c00; - m_beaconData.subType = 0x02; - m_beaconData.subTypeLength = 0x15; - m_beaconData.major = 0; - m_beaconData.minor = 0; - m_beaconData.signalPower = 0; - memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); -} // BLEBeacon - -std::string BLEBeacon::getData() { - return std::string((char*)&m_beaconData, sizeof(m_beaconData)); -} // getData - -void BLEBeacon::setMajor(uint16_t major) { - m_beaconData.major = ENDIAN_CHANGE_U16(major); -} // setMajor - -void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { - m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); -} // setManufacturerId - -void BLEBeacon::setMinor(uint16_t minor) { - m_beaconData.minor = ENDIAN_CHANGE_U16(minor); -} // setMinior - -void BLEBeacon::setProximityUUID(BLEUUID uuid) { - uuid = uuid.to128(); - memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); -} // setProximityUUID - -void BLEBeacon::setSignalPower(int8_t signalPower) { - m_beaconData.signalPower = signalPower; -} // setSignalPower - - - #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index c9d5ba98..003ad1a8 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -12,35 +12,6 @@ #include #include "BLEUUID.h" #include -#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) - -/** - * @brief Representation of a beacon. - * See: - * * https://en.wikipedia.org/wiki/IBeacon - */ -class BLEBeacon { -private: - struct { - uint16_t manufacturerId; - uint8_t subType; - uint8_t subTypeLength; - uint8_t proximityUUID[16]; - uint16_t major; - uint16_t minor; - int8_t signalPower; - } __attribute__((packed))m_beaconData; -public: - BLEBeacon(); - void setManufacturerId(uint16_t manufacturerId); - //void setSubType(uint8_t subType); - void setProximityUUID(BLEUUID uuid); - void setMajor(uint16_t major); - void setMinor(uint16_t minor); - void setSignalPower(int8_t signalPower); - std::string getData(); -}; // BLEBeacon - /** * @brief Advertisement data set by the programmer to be published by the %BLE server. diff --git a/cpp_utils/BLEBeacon.cpp b/cpp_utils/BLEBeacon.cpp new file mode 100644 index 00000000..a63197ca --- /dev/null +++ b/cpp_utils/BLEBeacon.cpp @@ -0,0 +1,84 @@ +/* + * BLEBeacon.cpp + * + * Created on: Jan 4, 2018 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEBeacon.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + +static const char LOG_TAG[] = "BLEBeacon"; + +BLEBeacon::BLEBeacon() { + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // BLEBeacon + +std::string BLEBeacon::getData() { + return std::string((char*)&m_beaconData, sizeof(m_beaconData)); +} // getData + +uint16_t BLEBeacon::getMajor() { + return m_beaconData.major; +} + +uint16_t BLEBeacon::getManufacturerId() { + return m_beaconData.manufacturerId; +} + +uint16_t BLEBeacon::getMinor() { + return m_beaconData.minor; +} + +BLEUUID BLEBeacon::getProximityUUID() { + return BLEUUID(m_beaconData.proximityUUID, 16, false); +} + +int8_t BLEBeacon::getSignalPower() { + return m_beaconData.signalPower; +} + +/** + * Set the raw data for the beacon record. + */ +void BLEBeacon::setData(std::string data) { + if (data.length() != sizeof(m_beaconData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); + return; + } + memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); +} // setData + +void BLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = ENDIAN_CHANGE_U16(major); +} // setMajor + +void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); +} // setManufacturerId + +void BLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); +} // setMinior + +void BLEBeacon::setProximityUUID(BLEUUID uuid) { + uuid = uuid.to128(); + memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); +} // setProximityUUID + +void BLEBeacon::setSignalPower(int8_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + + +#endif diff --git a/cpp_utils/BLEBeacon.h b/cpp_utils/BLEBeacon.h new file mode 100644 index 00000000..0b02e2bd --- /dev/null +++ b/cpp_utils/BLEBeacon.h @@ -0,0 +1,43 @@ +/* + * BLEBeacon2.h + * + * Created on: Jan 4, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ +#define COMPONENTS_CPP_UTILS_BLEBEACON_H_ +#include "BLEUUID.h" +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class BLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + int8_t signalPower; + } __attribute__((packed))m_beaconData; +public: + BLEBeacon(); + std::string getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + BLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(std::string data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(BLEUUID uuid); + void setSignalPower(int8_t signalPower); +}; // BLEBeacon + +#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ From c909117ea3baaba5c8e88f8d3d02310dfe97ce44 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 6 Jan 2018 16:14:05 -0600 Subject: [PATCH 155/310] Fixes for #344 --- cpp_utils/WebSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index 14642c34..285a0f1f 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -337,7 +337,7 @@ void WebSocket::send(std::string data, uint8_t sendType) { } else { frame.len = 126; m_socket.send((uint8_t *)&frame, sizeof(frame)); - m_socket.send((uint16_t)data.length()); + m_socket.send(htons((uint16_t)data.length())); // Convert to network byte order from host byte order } m_socket.send((uint8_t*)data.data(), data.length()); ESP_LOGD(LOG_TAG, "<< send"); From 137f3d997fde8fe15e66bb98984c1f187ebb2c54 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 9 Jan 2018 23:07:25 -0600 Subject: [PATCH 156/310] Fixes for #355 --- cpp_utils/HttpServer.cpp | 2 +- cpp_utils/Socket.cpp | 28 ++++++++++++++++++++++++---- cpp_utils/Socket.h | 5 +++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index a6d16e99..1b1845f1 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -131,7 +131,7 @@ class HttpServerTask: public Task { void run(void* data) { m_pHttpServer = (HttpServer*)data; // The passed in data is an instance of an HttpServer. m_pHttpServer->m_socket.setSSL(m_pHttpServer->m_useSSL); - m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber); + m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber, false /* is datagram */, true /* Allow address reuse */); ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort()); Socket clientSocket; while(1) { // Loop forever. diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index 602bdc74..89340ce6 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -259,9 +259,10 @@ bool Socket::isValid() { * @param [in] isDatagram True if we are listening on a datagram. The default is false. * @return Returns 0 on success. */ -int Socket::listen(uint16_t port, bool isDatagram) { +int Socket::listen(uint16_t port, bool isDatagram, bool reuseAddress) { ESP_LOGD(LOG_TAG, ">> listen: port: %d, isDatagram: %d", port, isDatagram); createSocket(isDatagram); + setReuseAddress(reuseAddress); int rc = bind(port, 0); if (rc != 0) { ESP_LOGE(LOG_TAG, "<< listen: Error in bind: %s", strerror(errno)); @@ -285,14 +286,19 @@ bool Socket::operator <(const Socket& other) const { return m_sock < other.m_sock; } -int Socket::setSocketOption(int option, char* value, size_t len) + +/** + * @brief Set the socket option. + */ +int Socket::setSocketOption(int option, void* value, size_t len) { - int res = setsockopt(m_sock, SOL_SOCKET, option, value, len); + int res = ::setsockopt(m_sock, SOL_SOCKET, option, value, len); if(res < 0) { ESP_LOGE(LOG_TAG, "%X : %d", option, errno); } return res; -} +} // setSocketOption + /** * @brief Socket timeout. @@ -479,6 +485,18 @@ void Socket::sendTo(const uint8_t* data, size_t length, struct sockaddr* pAddr) } // sendTo +/** + * @brief Flag the socket address as re-usable. + * @param [in] value True to mark the address as re-usable, false otherwise. + */ +void Socket::setReuseAddress(bool value) { + ESP_LOGD(LOG_TAG, ">> setReuseAddress: %d", value); + int val = value?1:0; + setSocketOption(SO_REUSEADDR, &val, sizeof(val)); + ESP_LOGD(LOG_TAG, "<< setReuseAddress"); +} // setReuseAddress + + /** * @brief Flag the socket as using SSL * @param [in] sslValue True if we wish to use SSL. @@ -651,3 +669,5 @@ SocketInputRecordStreambuf::int_type SocketInputRecordStreambuf::underflow() { SocketException::SocketException(int myErrno) { m_errno = myErrno; } + + diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 7685bf7c..6804ab93 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -68,13 +68,14 @@ class Socket { int connect(struct in_addr address, uint16_t port); int connect(char* address, uint16_t port); int createSocket(bool isDatagram = false); - int setSocketOption(int option, char* value, size_t len); + void setReuseAddress(bool value); + int setSocketOption(int option, void* value, size_t len); int setTimeout(uint32_t seconds); void getBind(struct sockaddr* pAddr); int getFD() const; bool getSSL() const; bool isValid(); - int listen(uint16_t port, bool isDatagram=false); + int listen(uint16_t port, bool isDatagram=false, bool reuseAddress=false); bool operator<(const Socket& other) const; std::string readToDelim(std::string delim); size_t receive(uint8_t* data, size_t length, bool exact=false); From 21264f79456aefcdb66540ab607151b6a9bb0bcd Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 14 Jan 2018 07:24:47 +0100 Subject: [PATCH 157/310] Use uart given as parameter to read line from GPS Previously the uart paramter has been ignored and UART1 has always been used to read a line from GPS. --- hardware/gps/Fragments/log_to_console.c | 2 +- hardware/gps/gps/main/gps.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware/gps/Fragments/log_to_console.c b/hardware/gps/Fragments/log_to_console.c index aa5ef034..be4717fd 100644 --- a/hardware/gps/Fragments/log_to_console.c +++ b/hardware/gps/Fragments/log_to_console.c @@ -8,7 +8,7 @@ char *readLine(uart_port_t uart) { int size; char *ptr = line; while(1) { - size = uart_read_bytes(UART_NUM_1, (unsigned char *)ptr, 1, portMAX_DELAY); + size = uart_read_bytes(uart, (unsigned char *)ptr, 1, portMAX_DELAY); if (size == 1) { if (*ptr == '\n') { *ptr = 0; diff --git a/hardware/gps/gps/main/gps.c b/hardware/gps/gps/main/gps.c index 917f412c..c54ae2bc 100644 --- a/hardware/gps/gps/main/gps.c +++ b/hardware/gps/gps/main/gps.c @@ -11,7 +11,7 @@ char *readLine(uart_port_t uart) { int size; char *ptr = line; while(1) { - size = uart_read_bytes(UART_NUM_1, (unsigned char *)ptr, 1, portMAX_DELAY); + size = uart_read_bytes(uart, (unsigned char *)ptr, 1, portMAX_DELAY); if (size == 1) { if (*ptr == '\n') { ptr++; From 59f8eb8048bfd5c94787d5593f1211c93e84f460 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Wed, 17 Jan 2018 18:05:17 -0600 Subject: [PATCH 158/310] Changes for #363 --- cpp_utils/WiFi.cpp | 18 ++++++++++++++---- cpp_utils/WiFi.h | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 25f9d1b5..6eeddc06 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -647,6 +647,7 @@ std::string WiFiAPRecord::toString() { return std::string(std::move(info_str)); } // toString +/* MDNS::MDNS() { esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); if (errRc != ESP_OK) { @@ -661,6 +662,7 @@ MDNS::~MDNS() { } m_mdns_server = nullptr; } +*/ /** * @brief Define the service for mDNS. @@ -670,6 +672,7 @@ MDNS::~MDNS() { * @param [in] port * @return N/A. */ +/* void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { serviceAdd(service.c_str(), proto.c_str(), port); } // serviceAdd @@ -688,7 +691,7 @@ void MDNS::servicePortSet(const std::string& service, const std::string& proto, void MDNS::serviceRemove(const std::string& service, const std::string& proto) { serviceRemove(service.c_str(), proto.c_str()); } // serviceRemove - +*/ /** * @brief Set the mDNS hostname. @@ -696,10 +699,11 @@ void MDNS::serviceRemove(const std::string& service, const std::string& proto) { * @param [in] hostname The host name to set against the mDNS. * @return N/A. */ +/* void MDNS::setHostname(const std::string& hostname) { setHostname(hostname.c_str()); } // setHostname - +*/ /** * @brief Set the mDNS instance. @@ -707,9 +711,11 @@ void MDNS::setHostname(const std::string& hostname) { * @param [in] instance The instance name to set against the mDNS. * @return N/A. */ +/* void MDNS::setInstance(const std::string& instance) { setInstance(instance.c_str()); } // setInstance +*/ /** * @brief Define the service for mDNS. @@ -719,6 +725,7 @@ void MDNS::setInstance(const std::string& instance) { * @param [in] port * @return N/A. */ +/* void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); if (errRc != ESP_OK) { @@ -754,13 +761,14 @@ void MDNS::serviceRemove(const char* service, const char* proto) { } } // serviceRemove - +*/ /** * @brief Set the mDNS hostname. * * @param [in] hostname The host name to set against the mDNS. * @return N/A. */ +/* void MDNS::setHostname(const char* hostname) { esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); if (errRc != ESP_OK) { @@ -768,7 +776,7 @@ void MDNS::setHostname(const char* hostname) { abort(); } } // setHostname - +*/ /** * @brief Set the mDNS instance. @@ -776,6 +784,7 @@ void MDNS::setHostname(const char* hostname) { * @param [in] instance The instance name to set against the mDNS. * @return N/A. */ +/* void MDNS::setInstance(const char* instance) { esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); if (errRc != ESP_OK) { @@ -783,3 +792,4 @@ void MDNS::setInstance(const char* instance) { abort(); } } // setInstance +*/ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index eec479d2..5d45d0d8 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -19,6 +19,7 @@ /** * @brief Manage mDNS server. */ +/* class MDNS { public: MDNS(); @@ -40,6 +41,7 @@ class MDNS { private: mdns_server_t *m_mdns_server = nullptr; }; +*/ class WiFiAPRecord { public: From 9d53b4211f991d5db2c3ee217ac5b699c7af0cf6 Mon Sep 17 00:00:00 2001 From: reaper7 Date: Thu, 18 Jan 2018 07:46:59 +0000 Subject: [PATCH 159/310] add missing BLE2904.* files for arduino build --- cpp_utils/Makefile.arduino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index 82d7da3e..07408ea9 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -10,6 +10,8 @@ BLE_FILES= \ BLE2902.cpp \ BLE2902.h \ + BLE2904.cpp \ + BLE2904.h \ BLEAddress.cpp \ BLEAddress.h \ BLEAdvertisedDevice.cpp \ From 2cb73c57a7fae282383929d96760f2bde72b92ed Mon Sep 17 00:00:00 2001 From: reaper7 Date: Thu, 18 Jan 2018 08:15:35 +0000 Subject: [PATCH 160/310] add missing BLEBeacon.* files --- cpp_utils/Makefile.arduino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index 07408ea9..b9524dcf 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -18,6 +18,8 @@ BLE_FILES= \ BLEAdvertisedDevice.h \ BLEAdvertising.cpp \ BLEAdvertising.h \ + BLEBeacon.cpp \ + BLEBeacon.h \ BLECharacteristic.cpp \ BLECharacteristic.h \ BLECharacteristicMap.cpp \ From bf2da87c2c91e976a52ff8b4302644e59e761ce5 Mon Sep 17 00:00:00 2001 From: "U-THINKPAD-T530\\springob" Date: Sat, 20 Jan 2018 15:39:58 +0100 Subject: [PATCH 161/310] Added Task::setCore(BaseType_t coreId) for setting the core number the task has to run on. --- cpp_utils/Task.cpp | 14 +++++++++++++- cpp_utils/Task.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index 4520478a..a8ba9a37 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -30,6 +30,7 @@ Task::Task(std::string taskName, uint16_t stackSize, uint8_t priority) { m_priority = priority; m_taskData = nullptr; m_handle = nullptr; + m_coreId = tskNO_AFFINITY; } // Task Task::~Task() { @@ -71,7 +72,7 @@ void Task::start(void* taskData) { ESP_LOGW(tag, "Task::start - There might be a task already running!"); } m_taskData = taskData; - ::xTaskCreate(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle); + ::xTaskCreatePinnedToCore(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle, m_coreId); } // start @@ -118,3 +119,14 @@ void Task::setPriority(uint8_t priority) { void Task::setName(std::string name) { m_taskName = name; } // setName + +/** + * @brief Set the core number the task has to be executed on. + * If the core number is not set, tskNO_AFFINITY will be used + * + * @param [in] coreId The id of the core. + * @return N/A. + */ +void Task::setCore(BaseType_t coreId) { + m_coreId = coreId; +} diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 74d7f332..0d58f222 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -38,6 +38,7 @@ class Task { void setStackSize(uint16_t stackSize); void setPriority(uint8_t priority); void setName(std::string name); + void setCore(BaseType_t coreId); void start(void* taskData=nullptr); void stop(); /** @@ -59,6 +60,7 @@ class Task { std::string m_taskName; uint16_t m_stackSize; uint8_t m_priority; + BaseType_t m_coreId; }; #endif /* COMPONENTS_CPP_UTILS_TASK_H_ */ From dee95597943c347636a2c7e3301ea4aa5fb05b58 Mon Sep 17 00:00:00 2001 From: Han Date: Thu, 25 Jan 2018 22:32:01 +0100 Subject: [PATCH 162/310] Handle partial writes for issue #377 --- cpp_utils/Socket.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index 89340ce6..bc5a064c 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -429,15 +429,36 @@ int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { int Socket::send(const uint8_t* data, size_t length) const { ESP_LOGD(LOG_TAG, "send: Raw binary of length: %d", length); //GeneralUtils::hexDump(data, length); - int rc; - if (getSSL()) { - rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); - } else { - rc = ::lwip_send_r(m_sock, data, length, 0); - } - if (rc == -1) { - ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); - } + int rc = ERR_OK; + while (length > 0) + { + if (getSSL()) { + rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); + // retry with same parameters if MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ + if ((rc != MBEDTLS_ERR_SSL_WANT_WRITE) && (rc != MBEDTLS_ERR_SSL_WANT_READ)) { + if (rc < 0) { + // no cure for other errors - log and exit + ESP_LOGE(LOG_TAG, "send: SSL write error %d", rc); + return rc; + } else { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } + } else { + rc = ::lwip_send_r(m_sock, data, length, 0); + if ((rc < 0) && (errno != EAGAIN)) { + // no cure for errors other than EAGAIN - log and exit + ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); + return rc; + } else if (rc > 0) { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } + } return rc; } // send From d650cfc7e0ba7bdec01cbe9693bfde56373204c2 Mon Sep 17 00:00:00 2001 From: Han Date: Thu, 25 Jan 2018 23:21:34 +0100 Subject: [PATCH 163/310] Added WebSocket::send for binary data (issue #380) --- cpp_utils/WebSocket.cpp | 29 +++++++++++++++++++++++++++++ cpp_utils/WebSocket.h | 1 + 2 files changed, 30 insertions(+) diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index 285a0f1f..e7fc05fb 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -344,6 +344,35 @@ void WebSocket::send(std::string data, uint8_t sendType) { } // send_cpp +/** + * @brief Send data down the web socket + * See the WebSocket spec (RFC6455) section "6.1 Sending Data". + * We build a WebSocket frame, send the frame followed by the data. + * @param [in] data The data to send down the WebSocket. + * @param [in] sendType The type of payload. Either SEND_TYPE_TEXT or SEND_TYPE_BINARY. + */ +void WebSocket::send(uint8_t* data, uint16_t length, uint8_t sendType) { + ESP_LOGD(LOG_TAG, ">> send: Length: %d", length); + Frame frame; + frame.fin = 1; + frame.rsv1 = 0; + frame.rsv2 = 0; + frame.rsv3 = 0; + frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; + frame.mask = 0; + if (length < 126) { + frame.len = length; + m_socket.send((uint8_t *)&frame, sizeof(frame)); + } else { + frame.len = 126; + m_socket.send((uint8_t *)&frame, sizeof(frame)); + m_socket.send(htons(length)); // Convert to network byte order from host byte order + } + m_socket.send(data, length); + ESP_LOGD(LOG_TAG, "<< send"); +} + + /** * @brief Set the Web socket handler associated with this Websocket. * diff --git a/cpp_utils/WebSocket.h b/cpp_utils/WebSocket.h index 80c72f0c..d3bfda49 100644 --- a/cpp_utils/WebSocket.h +++ b/cpp_utils/WebSocket.h @@ -91,6 +91,7 @@ class WebSocket { WebSocketHandler* getHandler(); Socket getSocket(); void send(std::string data, uint8_t sendType = SEND_TYPE_BINARY); + void send(uint8_t* data, uint16_t length, uint8_t sendType = SEND_TYPE_BINARY); void setHandler(WebSocketHandler *handler); }; // WebSocket From b1b56c7ef0b3cdfc6cbd1b109b2b24c48c8c52e2 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 27 Jan 2018 15:35:46 -0600 Subject: [PATCH 164/310] Changes for #270 --- cpp_utils/PCF8575.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++ cpp_utils/PCF8575.h | 37 +++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 cpp_utils/PCF8575.cpp create mode 100644 cpp_utils/PCF8575.h diff --git a/cpp_utils/PCF8575.cpp b/cpp_utils/PCF8575.cpp new file mode 100644 index 00000000..3714abb1 --- /dev/null +++ b/cpp_utils/PCF8575.cpp @@ -0,0 +1,123 @@ +/* + * PCF8575.cpp + * + * Created on: Jan 27, 2018 + * Author: kolban + */ + +#include "PCF8575.h" +#include "I2C.h" + +/** + * @brief Class constructor. + * + * The address is the address of the device on the %I2C bus. This is the value 0x20 plus + * the value of the device input pins `A0`, `A1` and `A2`. This means that the address should + * be between 0x20 and 0x27. + * + * @param [in] address The %I2C address of the device on the %I2C bus. + */ +PCF8575::PCF8575(uint8_t address) { + i2c.setAddress(address); + m_lastWrite = 0; +} + +/** + * @brief Class instance destructor. + */ +PCF8575::~PCF8575() { +} + + +/** + * @brief Read all the input bits from the device. + * @return A 16 bit value representing the values on each of the input pins. + */ +uint16_t PCF8575::read() { + uint16_t value; + i2c.beginTransaction(); + i2c.read((uint8_t*)&value,true); + i2c.read(((uint8_t*)&value) + 1,true); + i2c.endTransaction(); + return value; +} // read + + +/** + * @brief Read the logic level on a given pin. + * + * @param [in] bit The input pin of the device to read. Values are 0-15. + * @return True if the pin is high, false otherwise. Undefined if there is no signal on the pin. + */ +bool PCF8575::readBit(uint16_t bit) { + if (bit > 7) { + return false; + } + uint16_t value = read(); + return (value & (1<> 8) & 0xff, true); + i2c.endTransaction(); + m_lastWrite = value; +} // write + + +/** + * @brief Change the output value of a specific pin. + * + * The other bits beyond the one setting retain their values from the previous call to write() or + * previous calls to writeBit(). + * + * @param [in] bit The pin to have its value changed. The pin may be 0-15. + * @param [in] value The logic level to appear on the identified output pin. + */ +void PCF8575::writeBit(uint16_t bit, bool value) { + if (bit > 15) { + return; + } + if (invert) { + value = !value; + } + if (value) { + m_lastWrite |= (1<invert = value; +} // setInvert + + +/** + * @brief Initialize the PCF8575 device. + * + * @param [in] sdaPin The pin to use for the %I2C SDA functions. + * @param [in] clkPin The pin to use for the %I2C CLK functions. + */ +void PCF8575::init(gpio_num_t sdaPin, gpio_num_t clkPin) { + i2c.init(0, sdaPin, clkPin); +} // init diff --git a/cpp_utils/PCF8575.h b/cpp_utils/PCF8575.h new file mode 100644 index 00000000..467fae6c --- /dev/null +++ b/cpp_utils/PCF8575.h @@ -0,0 +1,37 @@ +/* + * PCF8575.h + * + * Created on: Jan 27, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_PCF8575_H_ +#define COMPONENTS_CPP_UTILS_PCF8575_H_ +#include "I2C.h" + +/** + * @brief Encapsulate a %PCF8575 device. + * + * The %PCF8575 is a 16 bit %GPIO expander attached to %I2C. It can read and write 16 bits of data + * and hence has 16 pins that can be used for input or output. + * + * @see [PCF8575 home page](http://www.ti.com/product/PCF8575) + */ +class PCF8575 { +public: + PCF8575(uint8_t address); + virtual ~PCF8575(); + void init(gpio_num_t sdaPin=I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin=I2C::DEFAULT_CLK_PIN); + uint16_t read(); + bool readBit(uint16_t bit); + void setInvert(bool value); + void write(uint16_t value); + void writeBit(uint16_t bit, bool value); + +private: + I2C i2c = I2C(); + uint8_t m_lastWrite; + bool invert = false; +}; + +#endif /* COMPONENTS_CPP_UTILS_PCF8575_H_ */ From c0a57d95272ef07fa70cecb2baf1e2a4b61ea1b7 Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Wed, 31 Jan 2018 15:37:01 -0800 Subject: [PATCH 165/310] Added Example main.c to networking/bootwifi/README.md based on comment by @chegewara https://github.com/nkolban/esp32-snippets/issues/245#issuecomment-350034132 --- networking/bootwifi/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/networking/bootwifi/README.md b/networking/bootwifi/README.md index 3d93f557..2f2a7c6c 100644 --- a/networking/bootwifi/README.md +++ b/networking/bootwifi/README.md @@ -13,6 +13,27 @@ This code is supplied in the form of an ESP-IDF module. In addition, BootWiFi h The logic uses C++ exception handling and hence C++ exception handling must be enabled in `make menuconfig`. +## Example +Below is a sample of a minimal main.cpp. +* If you run it on an esp32, it should create a new open wifi network with SSID: "ESP32". +* Connect to that network. +* Then open a browser and go to http://192.168.4.1. +```C++ +#include "BootWiFi.h" +#include "sdkconfig.h" + +extern "C" { + void app_main(void); +} + +BootWiFi *boot; + +void app_main(void) { + boot = new BootWiFi(); + boot->boot(); +} +``` + ## GPIO boot override To enable the ability to specify a GPIO pin to override known station information, compile the code with `-DBOOTWIFI_OVERRIDE_GPIO=` when `` is a GPIO pin number. If the pin is high at startup, then it will override. The pin is configured as pull-down low so it need not be artificially held low. The default is no override pin. From f323d99245a68904106f8f9b3abaa29dfbb634ec Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 4 Feb 2018 18:42:41 -0600 Subject: [PATCH 166/310] Changes for #389 --- cpp_utils/WiFi.cpp | 17 +++++++++++++---- networking/bootwifi/BootWiFi.cpp | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 6eeddc06..b4ed0edb 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -54,16 +54,20 @@ WiFi::WiFi() { m_eventLoopStarted = false; m_initCalled = false; - m_pWifiEventHandler = new WiFiEventHandler(); + //m_pWifiEventHandler = new WiFiEventHandler(); m_apConnected = false; // Are we connected to an access point? } // WiFi + /** * @brief Deletes the event handler that was used by the class */ WiFi::~WiFi() { - delete m_pWifiEventHandler; -} + if (m_pWifiEventHandler != nullptr) { + delete m_pWifiEventHandler; + m_pWifiEventHandler = nullptr; + } +} // ~WiFi /** @@ -229,7 +233,12 @@ void WiFi::dump() { WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. // Invoke the event handler. - esp_err_t rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + esp_err_t rc; + if (pWiFi->m_pWifiEventHandler != nullptr) { + rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + } else { + rc = ESP_OK; + } // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that // indicates we are waiting for a connection complete. diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index eaf1a4b3..aadc6d34 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -253,8 +253,12 @@ class BootWifiEventHandler: public WiFiEventHandler { }; +/** + * Boot WiFi + */ void BootWiFi::bootWiFi2() { ESP_LOGD(LOG_TAG, ">> bootWiFi2"); + // Check for a GPIO override which occurs when a physical Pin is high // during the test. This can force the ability to check for new configuration // even if the existing configured access point is available. @@ -306,17 +310,22 @@ void BootWiFi::setAccessPointCredentials(std::string ssid, std::string password) } // setAccessPointCredentials + +/** + * @brief Main entry point into booting WiFi + */ void BootWiFi::boot() { ESP_LOGD(LOG_TAG, ">> boot"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " | BootWiFi |"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " Access point credentials: %s/%s", m_ssid.c_str(), m_password.c_str()); - m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. + m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. bootWiFi2(); - m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. + m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. + m_wifi.setWifiEventHandler(nullptr); // Remove the WiFi boot handler when we have completed booting. ESP_LOGD(LOG_TAG, "<< boot"); -} +} // boot BootWiFi::BootWiFi() { m_httpServerStarted = false; From c14df1aa58acccabb2cc365fd341837d22b50d75 Mon Sep 17 00:00:00 2001 From: Jack Rickard Date: Tue, 13 Feb 2018 22:44:09 -0600 Subject: [PATCH 167/310] Update BLEAdvertising.cpp Apple iBeacon requires a max and min advertising interval of 100ms. It appears to me we are kind of hard coded at 32ms min and 64ms max. We could probably do an iBeacon using setManufacturersData() and setFlags() but we need to be able to modify advertising interval to truly accommodate iBeacon spec. I think this code does it. --- cpp_utils/BLEAdvertising.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 9e01ab78..4b3cb8d6 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -91,6 +91,16 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { m_advData.appearance = appearance; } // setAppearance +void BLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advData.min_interval = mininterval; + m_advParams.adv_int_min = mininterval; +} // setMinInterval + +void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advData.max_interval = maxinterval; + m_advParams.adv_int_max = maxinterval; +} // setMaxInterval + /** * @brief Set the filtering for the scan filter. From d84d43ac4e50adf3e926c1efbdec3e722b4291c8 Mon Sep 17 00:00:00 2001 From: Jack Rickard Date: Tue, 13 Feb 2018 22:46:17 -0600 Subject: [PATCH 168/310] Update BLEAdvertising.h Add public methods to set minimum and maximum advertising packet transmission interval. --- cpp_utils/BLEAdvertising.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 003ad1a8..d1fa3c73 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -52,6 +52,8 @@ class BLEAdvertising { void start(); void stop(); void setAppearance(uint16_t appearance); + void setMaxInterval(uint16_t maxinterval); + void setMinInterval(uint16_t mininterval); void setAdvertisementData(BLEAdvertisementData& advertisementData); void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); void setScanResponseData(BLEAdvertisementData& advertisementData); From 0bd09ae2c2a8bb03c9e1eaac2c6b51318cc0cd17 Mon Sep 17 00:00:00 2001 From: Marcel Seerig Date: Fri, 16 Feb 2018 19:57:04 +0100 Subject: [PATCH 169/310] little changes for a better handling with the wifi class --- cpp_utils/WiFi.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++-- cpp_utils/WiFi.h | 7 ++++ 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index b4ed0edb..f3176530 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -292,6 +292,38 @@ std::string WiFi::getApSSID() { return std::string((char *)conf.sta.ssid); } // getApSSID +/** + * @brief Get the current ESP32 IP form AP. + * @return The ESP32 IP. + */ +std::string WiFi::getApIp(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + +/** + * @brief Get the current AP netmask. + * @return The Netmask IP. + */ +std::string WiFi::getApNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + +/** + * @brief Get the current AP Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getApGateway(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway /** * @brief Lookup an IP address by host name. @@ -351,6 +383,40 @@ tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { return ipInfo; } // getStaIpInfo +/** + * @brief Get the current ESP32 IP form STA. + * @return The ESP32 IP. + */ +std::string WiFi::getStaIp(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + + +/** + * @brief Get the current STA netmask. + * @return The Netmask IP. + */ +std::string WiFi::getStaNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + + +/** + * @brief Get the current STA Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getStaGateway(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway /** * @brief Get the MAC address of the STA interface. @@ -501,6 +567,27 @@ std::vector WiFi::scan() { * @return N/A. */ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { + startAP(ssid, password, auth, 0, false, 4); +} // startAP + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @param[in] channel from the access point. + * @param[in] is the ssid hidden, ore not. + * @param[in] limiting number of clients. + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); init(); @@ -517,10 +604,10 @@ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_au ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); apConfig.ap.ssid_len = ssid.size(); ::memcpy(apConfig.ap.password, password.data(), password.size()); - apConfig.ap.channel = 0; + apConfig.ap.channel = channel; apConfig.ap.authmode = auth; - apConfig.ap.ssid_hidden = 0; - apConfig.ap.max_connection = 4; + apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; + apConfig.ap.max_connection = max_connection; apConfig.ap.beacon_interval = 100; errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); @@ -543,7 +630,6 @@ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_au ESP_LOGD(LOG_TAG, "<< startAP"); } // startAP - /** * @brief Set the event handler to use to process detected events. * @param[in] wifiEventHandler The class that will be used to process events. diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 5d45d0d8..b06635d9 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -135,12 +135,19 @@ class WiFi { static std::string getApMac(); static tcpip_adapter_ip_info_t getApIpInfo(); static std::string getApSSID(); + static std::string getApIp(); + static std::string getApNetmask(); + static std::string getApGateway(); static std::string getMode(); static tcpip_adapter_ip_info_t getStaIpInfo(); static std::string getStaMac(); static std::string getStaSSID(); + static std::string getStaIp(); + static std::string getStaNetmask(); + static std::string getStaGateway(); std::vector scan(); void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); void setIPInfo(const char* ip, const char* gw, const char* netmask); void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); From bcf522e483c3029b098eb89d9ddad989fd6f247a Mon Sep 17 00:00:00 2001 From: Marcel Seerig Date: Mon, 19 Feb 2018 11:40:50 +0100 Subject: [PATCH 170/310] add the PubSubClient class. (alternative to AWS) --- cpp_utils/PubSubClient.cpp | 853 +++++++++++++++++++++++++++++++++++++ cpp_utils/PubSubClient.h | 172 ++++++++ 2 files changed, 1025 insertions(+) create mode 100644 cpp_utils/PubSubClient.cpp create mode 100644 cpp_utils/PubSubClient.h diff --git a/cpp_utils/PubSubClient.cpp b/cpp_utils/PubSubClient.cpp new file mode 100644 index 00000000..e3542cb0 --- /dev/null +++ b/cpp_utils/PubSubClient.cpp @@ -0,0 +1,853 @@ +/* + PubSubClient.cpp - A simple client for MQTT. + Nick O'Leary + http://knolleary.net + + edit by marcel.seerig + */ +#include "esp_log.h" + +#include "PubSubClient.h" +#include "Task.h" +#include "FreeRTOS.h" +#include "FreeRTOSTimer.h" + +#include "sdkconfig.h" + +static const char* TAG = "PubSubClient"; + +#define pgm_read_byte_near(x) *(x) + +/** + * @brief A task that will handle the PubSubClient. + * + * This Task is started, when we have a valid connection. + * If the connection breaks, we stop this Task. + */ +class PubSubClientTask: public Task { +public: + PubSubClientTask(std::string name) : + Task(name, 16 * 1024) { + taskName = name; + }; +private: + std::string taskName; + /** + * @brief Loop over the PubSubClient. + * @param [in] data A pointer to an instance of the PubSubClient. + */ + void run(void* data) { + PubSubClient* pPubSubClient = (PubSubClient*) data; + ESP_LOGD("PubSubClientTask", "PubSubClientTask Task started!"); + + while(1) { + if (pPubSubClient->connected()) { + + uint16_t len = pPubSubClient->readPacket(); + + if (len > 0) { // if there was data + + pPubSubClient->keepAliveTimer->reset(0); //lastInActivity = t; + + mqtt_message *msg = new mqtt_message; + pPubSubClient->parseData(msg, len); + + //pPubSubClient->dumpData(msg); + ESP_LOGD(TAG, "Message type (%s)!", pPubSubClient->messageType_toString(msg->type).c_str()); + + + if (msg->type == PUBLISH) { + + if (pPubSubClient->callback) { + + if (msg->qos == QOS0) { + + pPubSubClient->callback(msg->topic, msg->payload); + + } else if (msg->qos == QOS1) { + pPubSubClient->callback(msg->topic, msg->payload); + + pPubSubClient->buffer[0] = PUBACK; + pPubSubClient->buffer[1] = 2; + pPubSubClient->buffer[2] = (msg->msgId >> 8); + pPubSubClient->buffer[3] = (msg->msgId & 0xFF); + + int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 4); + if(rc < 0) pPubSubClient->_state = CONNECTION_LOST; + + pPubSubClient->keepAliveTimer->reset(0); //lastOutActivity = t; + + }else if(msg->qos == QOS2) { + ESP_LOGD(TAG, "QOS2 is not supported!"); + }else{ + ESP_LOGD(TAG, "QOS-Level unkonwon yet!"); + } + + } + } else if (msg->type == PINGREQ) { + pPubSubClient->buffer[0] = PINGRESP; + pPubSubClient->buffer[1] = 0; + int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 2); + if(rc < 0) pPubSubClient->_state = CONNECTION_LOST; + + } else if (msg->type == PINGRESP) { + pPubSubClient->PING_outstanding = false; + + }else if (msg->type == SUBACK) { + pPubSubClient->SUBACK_outstanding = false; + pPubSubClient->timeoutTimer->stop(0); + + }else if (msg->type == UNSUBACK) { + pPubSubClient->UNSUBACK_Outstanding = false; + pPubSubClient->timeoutTimer->stop(0); + } + + delete(msg); + } + } + } // While (1) + } // run +}; // PubSubClientTask + +PubSubClient::PubSubClient() { + setup(); + this->_state = DISCONNECTED; + this->_client = new Socket; + setCallback(NULL); +} + +PubSubClient::PubSubClient(Socket& client) { + setup(); + this->_state = DISCONNECTED; + setClient(client); +} + +PubSubClient::PubSubClient(std::string addr, uint16_t port) { + setup(); + this->_state = DISCONNECTED; + this->_client = new Socket; + setServer(addr, port); +} + +PubSubClient::PubSubClient(std::string addr, uint16_t port, Socket& client) { + setup(); + this->_state = DISCONNECTED; + setServer(addr, port); + setClient(client); +} + +PubSubClient::PubSubClient(std::string addr, uint16_t port, + MQTT_CALLBACK_SIGNATURE, Socket& client) { + setup(); + this->_state = DISCONNECTED; + setServer(addr, port); + setCallback(callback); + setClient(client); +} + +PubSubClient::~PubSubClient() { + _client->close(); + keepAliveTimer->stop(0); + timeoutTimer->stop(0); + m_task->stop(); + delete (_client); + delete (keepAliveTimer); + delete (timeoutTimer); + delete (m_task); +} + +/** + * @brief This is a Timer called routine mapping routine, which calls + * the PubSubClient member function keepAliveChecker. + * @param The FreeRTOSTimer root instance for this callback function. + * @return N/A. + */ +void keepAliveTimerMapper(FreeRTOSTimer *pTimer) { + PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); + m_pubSubClient->keepAliveChecker(); +} //keepAliveChecker + +/** + * @brief This is a Timer called routine mapping routine, which calls + * the PubSubClient member function timeoutChecker. + * @param The FreeRTOSTimer root instance for this callback function. + * @return N/A. + */ +void timeoutTimerMapper(FreeRTOSTimer *pTimer) { + PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); + m_pubSubClient->timeoutChecker(); +} //keepAliveChecker + +/** + * @brief This is a internal setup routine for the PubSubClient. + * @param N/A. + * @return N/A. + */ +void PubSubClient::setup(void) { + PING_outstanding = false; + SUBACK_outstanding = false; + UNSUBACK_Outstanding = false; + + + keepAliveTimer = new FreeRTOSTimer((char*) "keepAliveTimer", + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, true, this, + keepAliveTimerMapper); + timeoutTimer = new FreeRTOSTimer((char*) "timeoutTimer", + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, true, this, + timeoutTimerMapper); + m_task = new PubSubClientTask("PubSubClientTask"); +} // setup + +/** + * @brief This is a Timer called routine, which checks the PING_outstanding flag. + * This flag is set in this function. We send a Keep alive message to the + * server here. If there is a data in, or output, or we receive the MQTT + * ping request, the flag will be set to false by other functions. + * This function is called every MQTT_KEEPALIVE interval. If the flag is + * still true, we have a error with the connection. + * @param N/A. + * @return N/A. + */ +void PubSubClient::keepAliveChecker(void){ + + if (PING_outstanding && connected()) { + _state = CONNECTION_TIMEOUT; + //_client->close(); + ESP_LOGD(TAG, "KeepAlive TIMEOUT!"); + } else { + buffer[0] = PINGREQ; + buffer[1] = 0; + int rc = _client->send(buffer, 2); + if(rc < 0) _state = CONNECTION_LOST; + ESP_LOGD(TAG, "send KeepAlive REQUEST!"); + PING_outstanding = true; + } +} //keepAliveChecker + +/** + * @brief This is a Timer called routine, which is called, when we reach the timeout. + * Used is this function for all ACK commands, which comes over MQTT. Notice, + * that the KeepAlivePing has his own Timer, to prevent conflicts. Basically, if + * this function is called, we have detected a MQTT timeout! + * @param N/A. + * @return N/A. + */ +void PubSubClient::timeoutChecker(void){ + + if (connected() && (SUBACK_outstanding || UNSUBACK_Outstanding)) { + _state = CONNECTION_TIMEOUT; + //_client->close(); + ESP_LOGD(TAG, "MQTT TIMEOUT!"); + } +} //keepAliveChecker + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char *id) { + return connect(id, NULL, NULL, 0, 0, 0, 0); +} + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] my user name. + * [in] my password. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char *id, const char *user, const char *pass) { + return connect(id, user, pass, 0, 0, 0, 0); +} + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] last will: topic name. + * [in] last will: qos-level. + * [in] last will: as retained message. + * [in] last will: payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char *id, const char* willTopic, + uint8_t willQos, bool willRetain, const char* willMessage) { + return connect(id, NULL, NULL, willTopic, willQos, willRetain, willMessage); +} + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] my user name. + * [in] my password. + * [in] last will: topic name. + * [in] last will: qos-level. + * [in] last will: as retained message. + * [in] last will: payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char *id, const char *user, const char *pass, + const char* willTopic, uint8_t willQos, bool willRetain, + const char* willMessage) { + _config.id = id; + + _config.user = user; + _config.pass = pass; + + _config.willTopic = willTopic; + _config.willQos = willQos; + _config.willRetain = willRetain; + _config.willMessage = willMessage; + + return connect(); +} + +/** + * @brief Connect to a MQTT server with the with the previous settings. + * Note: do not call this function without settings, this will not work! + * For the very first connect process, use the connect function with parameters. + * @param N/A + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(){ + + if (!connected()) { + + ESP_LOGD(TAG, "Connect to mqtt server..."); + + ESP_LOGD(TAG, "ip: %s port: %d", _config.ip.c_str(), _config.port) + int result = _client->connect((char *)_config.ip.c_str(), _config.port); + + if (result == 0) { + + nextMsgId = 1; + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + unsigned int j; + +#if MQTT_VERSION == MQTT_VERSION_3_1 + uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION}; +#define MQTT_HEADER_VERSION_LENGTH 9 +#elif MQTT_VERSION == MQTT_VERSION_3_1_1 + uint8_t d[7] = { 0x00, 0x04, 'M', 'Q', 'T', 'T', MQTT_VERSION }; +#define MQTT_HEADER_VERSION_LENGTH 7 +#endif + for (j = 0; j < MQTT_HEADER_VERSION_LENGTH; j++) { + buffer[length++] = d[j]; + } + + uint8_t v; + if (_config.willTopic) { + v = 0x06 | (_config.willQos << 3) | (_config.willRetain << 5); + } else { + v = 0x02; + } + + if (_config.user != NULL) { + v = v | 0x80; + + if (_config.pass != NULL) { + v = v | (0x80 >> 1); + } + } + + buffer[length++] = v; + + buffer[length++] = ((MQTT_KEEPALIVE) >> 8); + buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + length = writeString(_config.id, buffer, length); + if (_config.willTopic) { + length = writeString(_config.willTopic, buffer, length); + length = writeString(_config.willMessage, buffer, length); + } + + if (_config.user != NULL) { + length = writeString(_config.user, buffer, length); + if (_config.pass != NULL) { + length = writeString(_config.pass, buffer, length); + } + } + + write(CONNECT, buffer, length - 5); + + // start keepAliveTimer in 1ms... + keepAliveTimer->start(0); //lastInActivity = lastOutActivity = millis(); + + readPacket(); + uint8_t type = buffer[0] & 0xF0; + + if (type == CONNACK) { + ESP_LOGD(TAG, "Connected to mqtt server!"); + + keepAliveTimer->reset(0); //lastInActivity = millis(); + PING_outstanding = false; + _state = CONNECTED; + + m_task->start(this); + return true; + } else { + _state = (mqtt_state) buffer[3]; + ESP_LOGD(TAG, "Error: %d", _state); + } + + } else { + keepAliveTimer->stop(0); + _state = CONNECT_FAILED; + } + return false; + } + return true; +} + +/** + * @brief Receiving a MQTT packet and store it in the buffer to parse it. + * @param N/A. + * @return Number of received bytes. + */ +uint16_t PubSubClient::readPacket() { + + size_t res = _client->receive(buffer, MQTT_MAX_PACKET_SIZE); + + if (res > MQTT_MAX_PACKET_SIZE) { + res = 0; // This will cause the packet to be ignored. + } + + return res; +} + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const char* payload) { + return publish(topic, (const uint8_t*) payload, strlen(payload), false); +} + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] is this a retained message (true/false) + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const char* payload, + bool retained) { + return publish(topic, (const uint8_t*) payload, strlen(payload), retained); +} + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] length of the message + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const uint8_t* payload, + unsigned int plength) { + return publish(topic, payload, plength, false); +} + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] length of the message + * [in] is this a retained message (true/false) + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const uint8_t* payload, + unsigned int plength, bool retained) { + if (connected()) { + if (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength) { + // Too long + return false; + } + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + length = writeString(topic, buffer, length); + uint16_t i; + for (i = 0; i < plength; i++) { + buffer[length++] = payload[i]; + } + uint8_t header = PUBLISH; + if (retained) { + header |= 1; + } + return write(header, buffer, length - 5); + } + return false; +} + +//bool PubSubClient::publish_P(const char* topic, const uint8_t* payload, +// unsigned int plength, bool retained) { +// uint8_t llen = 0; +// uint8_t digit; +// unsigned int rc = 0; +// uint16_t tlen; +// unsigned int pos = 0; +// unsigned int i; +// uint8_t header; +// unsigned int len; +// +// if (!connected()) { +// return false; +// } +// +// tlen = strlen(topic); +// +// header = PUBLISH; +// if (retained) { +// header |= 1; +// } +// buffer[pos++] = header; +// len = plength + 2 + tlen; +// do { +// digit = len % 128; +// len = len / 128; +// if (len > 0) { +// digit |= 0x80; +// } +// buffer[pos++] = digit; +// llen++; +// } while (len > 0); +// +// pos = writeString(topic, buffer, pos); +// +// rc += _client->send(buffer, pos); +// +// for (i = 0; i < plength; i++) { +// //rc += _client->send((char)pgm_read_byte_near(payload + i)); +// //rc += _client->send((uint8_t*) pgm_read_byte_near(payload + i), 1); +// } +// +// keepAliveTimer->reset(0); //lastOutActivity = millis(); +// +// return rc == tlen + 4 + plength; +//} + +/** + * @brief Send a MQTT message over socket. + * @param [in] MQTT header. + * [in] Message buffer. + * [in] total length. + * @return success (true), or no success (false). + */ +bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { + uint8_t lenBuf[4]; + uint8_t llen = 0; + uint8_t digit; + uint8_t pos = 0; + int rc; + uint16_t len = length; + do { + digit = len % 128; + len = len / 128; + if (len > 0) { + digit |= 0x80; + } + lenBuf[pos++] = digit; + llen++; + } while (len > 0); + + buf[4 - llen] = header; + for (int i = 0; i < llen; i++) { + buf[5 - llen + i] = lenBuf[i]; + } + +//#ifdef MQTT_MAX_TRANSFER_SIZE +// uint8_t* writeBuf = buf+(4-llen); +// uint16_t bytesRemaining = length+1+llen; //Match the length type +// uint8_t bytesToWrite; +// bool result = true; +// while((bytesRemaining > 0) && result) { +// bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; +// rc = _client->write(writeBuf,bytesToWrite); +// result = (rc == bytesToWrite); +// bytesRemaining -= rc; +// writeBuf += rc; +// } +// return result; +//#else + rc = _client->send(buf + (4 - llen), length + 1 + llen); + if(rc < 0) _state = CONNECTION_LOST; + keepAliveTimer->reset(0); //lastOutActivity = millis(); + return (rc == 1 + llen + length); +//#endif +} + +/** + * @brief Subscribe a MQTT topic. + * @param [in] my topic + * [in] qos of subscription + * @return request transmitted with success (true), or no success (false). + */ +bool PubSubClient::subscribe(const char* topic, bool ack) { + + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer, length); + buffer[length++] = QOS1; + + if(write(SUBSCRIBE | QOS1, buffer, length - 5)){ + SUBACK_outstanding = true; + if(ack) timeoutTimer->start(0); + return true; + } + } + return false; +} + +/** + * @brief Check the state of subscription. If there was received a subscription + * ACK, we return a true here. + * @return Is subscription validated with ACK (true/false) + */ +bool PubSubClient::isSubscribeDone(void){ + return !SUBACK_outstanding; +} + +/** + * @brief Unsubscribe a MQTT topic. + * @param [in] my topic + * [in] qos of unsubscription + * @return request transmitted with success (true), or no success (false). + */ +bool PubSubClient::unsubscribe(const char* topic, bool ack) { + + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer, length); + + if(write(UNSUBSCRIBE | QOS1, buffer, length - 5)){ + UNSUBACK_Outstanding = true; + if(ack) timeoutTimer->start(0); + return true; + } + } + return false; +} + +/** + * @brief Check the state of unsubscription. If there was received a unsubscription + * ACK, we return a true here. + * @return Is unsubscription validated with ACK (true/false) + */ +bool PubSubClient::isUnsubscribeDone(void){ + return !UNSUBACK_Outstanding; +} + +/** + * @brief Disconnect form MQTT server and close the socket. + * @return N/A. + */ +void PubSubClient::disconnect() { + buffer[0] = DISCONNECT; + buffer[1] = 0; + _client->send(buffer, 2); + _state = DISCONNECTED; + _client->close(); + keepAliveTimer->stop(0); //lastInActivity = lastOutActivity = millis(); + timeoutTimer->stop(0); +} + +/** + * @brief calculation help to send a string. + */ +uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, + uint16_t pos) { + const char* idp = string; + uint16_t i = 0; + pos += 2; + while (*idp) { + buf[pos++] = *idp++; + i++; + } + buf[pos - i - 2] = (i >> 8); + buf[pos - i - 1] = (i & 0xFF); + return pos; +} + +/** + * @brief Check the connection to the MQTT server. + * @return connected (true/false) + */ +bool PubSubClient::connected() { + + if (this->_state == CONNECTED) { + + bool rc = true; + + if (_client == nullptr) { + rc = false; + } else if (!_client->isValid()) { + rc = false; + + this->_state = CONNECTION_LOST; + + if (_client->isValid()) _client->close(); + keepAliveTimer->stop(0); + timeoutTimer->stop(0); + } + return rc; + } + return false; + ESP_LOGD(TAG, "_state = %d", _state); + +} + +/** + * @brief Set server ip and Port of my MQTT server. + * @param [in] ip of the distant MQTT server. + * [in] the port of the distant MQTT port. + * @return My instance. + */ +PubSubClient& PubSubClient::setServer(std::string ip, uint16_t port) { + _config.ip = ip; + _config.port = port; + return *this; +} + +/** + * @brief Set the callback function for incoming data. + * @param [in] callback function + * @return My instance. + */ +PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) { + this->callback = callback; + return *this; +} + +/** + * @brief Set the socket, which we want to use for our MQTT communication. + * @param [in] the new socket instance + * @return My instance. + */ +PubSubClient& PubSubClient::setClient(Socket& client) { + this->_client = &client; + return *this; +} + +/** + * @brief Get the current MYTT state form the instance. + * @param N/A. + * @return current MQTT state. + */ +int PubSubClient::state() { + return this->_state; +} + +/** + * @brief Parsing the received data in to the internal message struct. + */ +void PubSubClient::parseData(mqtt_message* msg, uint16_t len){ + + /********* Parse Fixed header *********/ + msg->type = buffer[0] & 0xF0; + + /* read DUP-Flag */ + if(msg->type == PUBLISH) + msg->dup = (bool)(buffer[0] & 0x18)>>3; + + /* read QoS-Level */ + if(msg->type == PUBLISH) + msg->qos = (buffer[0] & 0x06); + + /* read RETAIN-Frag */ + if(msg->type == PUBLISH) + msg->retained = (bool)(buffer[0] & 0x01); + + uint8_t remainingLength = buffer[1]; + + /********* Parse Variable header *********/ + int pos = 2; + + /* read topic name */ + if(msg->type == PUBLISH){ + uint16_t topicLen = (buffer[2] << 8) + buffer[3]; + + msg->topic = ""; + for(int i = 4; i<(topicLen+4); i++){ + msg->topic += (char) buffer[i]; + pos++; + } + } + + /* read Message ID */ + if(msg->type == PUBLISH || msg->type == PUBACK || msg->type == PUBREC || msg->type == PUBCOMP || msg->type == SUBACK || msg->type == UNSUBACK){ + msg->msgId = (buffer[pos]<<8) + (buffer[pos+1]); + pos += 2; + } + + /********* read Payload *********/ + if(msg->type == PUBLISH){ + msg->payload = ""; + for(int i = pos; i payload += (char)buffer[i]; + } + } +} + +/** + * @brief Dump the message struct. + */ +void PubSubClient::dumpData(mqtt_message* msg){ + + ESP_LOGD(TAG, "mqtt_message_type: %s", messageType_toString(msg->type).c_str()); + ESP_LOGD(TAG, "mqtt_qos: %d", msg->qos); + ESP_LOGD(TAG, "retained: %d", msg->retained); + ESP_LOGD(TAG, "dup: %d", msg->dup); + ESP_LOGD(TAG, "topic: %s", msg->topic.c_str()); + ESP_LOGD(TAG, "payload: %s", msg->payload.c_str()); + ESP_LOGD(TAG, "msgId: %d", msg->msgId); +} + +/** + * @brief Convert the MQTT message type to string. + * @param [in] message type byte. + * @return message type as std::string. + */ +std::string PubSubClient::messageType_toString(uint8_t type){ + std::string str = "Not in list!"; + switch(type){ + case CONNECT : str = "CONNECT"; break; + case CONNACK : str = "CONNACK"; break; + case PUBLISH : str = "PUBLISH"; break; + case PUBACK : str = "PUBACK"; break; + case PUBREC : str = "PUBREC"; break; + case PUBREL : str = "PUBREL"; break; + case PUBCOMP : str = "PUBCOMP"; break; + case SUBSCRIBE : str = "SUBSCRIBE"; break; + case SUBACK : str = "SUBACK"; break; + case UNSUBSCRIBE: str = "UNSUBSCRIBE"; break; + case UNSUBACK : str = "UNSUBACK"; break; + case PINGREQ : str = "PINGREQ"; break; + case PINGRESP : str = "PINGRESP"; break; + case DISCONNECT : str = "DISCONNECT"; break; + case Reserved : str = "Reserved"; break; + } + + return str; +} diff --git a/cpp_utils/PubSubClient.h b/cpp_utils/PubSubClient.h new file mode 100644 index 00000000..8e56ca59 --- /dev/null +++ b/cpp_utils/PubSubClient.h @@ -0,0 +1,172 @@ +/* + PubSubClient.h - A simple client for MQTT. + Nick O'Leary + http://knolleary.net + + edit by marcel.seerig +*/ + +#ifndef PubSubClient_h +#define PubSubClient_h + +#include +#include "Socket.h" +#include "FreeRTOSTimer.h" + +#define MQTT_VERSION_3_1 3 +#define MQTT_VERSION_3_1_1 4 + +// MQTT_VERSION : Pick the version +//#define MQTT_VERSION MQTT_VERSION_3_1 +#ifndef MQTT_VERSION +#define MQTT_VERSION MQTT_VERSION_3_1_1 +#endif + +// MQTT_MAX_PACKET_SIZE : Maximum packet size +#ifndef MQTT_MAX_PACKET_SIZE +#define MQTT_MAX_PACKET_SIZE 128 +#endif + +// MQTT_KEEPALIVE : keepAlive interval in Seconds +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 15 +#endif + +// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds +#ifndef MQTT_SOCKET_TIMEOUT +#define MQTT_SOCKET_TIMEOUT 15 +#endif + +// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client +// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to +// pass the entire MQTT packet in each write call. +//#define MQTT_MAX_TRANSFER_SIZE 80 + +// Possible values for client.state() + +struct mqtt_InitTypeDef{ + std::string ip; + uint16_t port; + + const char* user; + const char* pass; + + const char * id; + const char* willTopic; + uint8_t willQos; + bool willRetain; + const char* willMessage; +}; + +typedef enum{ + CONNECTION_TIMEOUT = -4, + CONNECTION_LOST = -3, + CONNECT_FAILED = -2, + DISCONNECTED = -1, + CONNECTED = 0, + CONNECT_BAD_PROTOCOL = 1, + CONNECT_BAD_CLIENT_ID = 2, + CONNECT_UNAVAILABLE = 3, + CONNECT_BAD_CREDENTIALS = 4, + CONNECT_UNAUTHORIZED = 5, +}mqtt_state; + +typedef enum{ + CONNECT = 1 << 4, // Client request to connect to Server + CONNACK = 2 << 4, // Connect Acknowledgment + PUBLISH = 3 << 4, // Publish message + PUBACK = 4 << 4, // Publish Acknowledgment + PUBREC = 5 << 4, // Publish Received (assured delivery part 1) + PUBREL = 6 << 4, // Publish Release (assured delivery part 2) + PUBCOMP = 7 << 4, // Publish Complete (assured delivery part 3) + SUBSCRIBE = 8 << 4, // Client Subscribe request + SUBACK = 9 << 4, // Subscribe Acknowledgment + UNSUBSCRIBE = 10 << 4, // Client Unsubscribe request + UNSUBACK = 11 << 4, // Unsubscribe Acknowledgment + PINGREQ = 12 << 4, // PING Request + PINGRESP = 13 << 4, // PING Response + DISCONNECT = 14 << 4, // Client is Disconnecting + Reserved = 15 << 4, // Reserved +}mqtt_message_type; + +typedef enum{ + QOS0 = (0 << 1), + QOS1 = (1 << 1), + QOS2 = (2 << 1), +}mqtt_qos; + +struct mqtt_message{ + uint8_t type; + uint8_t qos; + bool retained; + bool dup; + std::string topic; + std::string payload; + uint16_t msgId; +}; + +#define MQTT_CALLBACK_SIGNATURE void (*callback)(std::string, std::string) + +class PubSubClientTask; + +class PubSubClient { +public: + PubSubClient(); + PubSubClient(Socket& client); + PubSubClient(std::string ip, uint16_t port); + PubSubClient(std::string ip, uint16_t port, Socket& client); + PubSubClient(std::string ip, uint16_t port, MQTT_CALLBACK_SIGNATURE,Socket& client); + ~PubSubClient(); + + PubSubClient& setServer(std::string ip, uint16_t port); + PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); + PubSubClient& setClient(Socket& client); + + bool connect(const char* id); + bool connect(const char* id, const char* user, const char* pass); + bool connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage); + bool connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage); + bool connect(); + void disconnect(); + bool publish(const char* topic, const char* payload); + bool publish(const char* topic, const char* payload, bool retained); + bool publish(const char* topic, const uint8_t * payload, unsigned int plength); + bool publish(const char* topic, const uint8_t * payload, unsigned int plength, bool retained); + //bool publish_P(const char* topic, const uint8_t * payload, unsigned int plength, bool retained); + + bool subscribe (const char* topic, bool ack=false); + bool unsubscribe (const char* topic, bool ack=false); + bool isSubscribeDone (void); + bool isUnsubscribeDone (void); + + bool connected (void); + int state (void); + void keepAliveChecker (void); + void timeoutChecker (void); + +private: + friend class PubSubClientTask; + PubSubClientTask* m_task; + Socket* _client; + mqtt_InitTypeDef _config; + mqtt_state _state; + uint8_t buffer[MQTT_MAX_PACKET_SIZE]; + uint16_t nextMsgId; + bool PING_outstanding; + bool SUBACK_outstanding; + bool UNSUBACK_Outstanding; + FreeRTOSTimer* keepAliveTimer; + FreeRTOSTimer* timeoutTimer; + + MQTT_CALLBACK_SIGNATURE; + void setup (void); + uint16_t readPacket(); + bool write (uint8_t header, uint8_t* buf, uint16_t length); + uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + void parseData (mqtt_message* msg, uint16_t len); + void dumpData (mqtt_message* msg); + std::string messageType_toString(uint8_t type); + +}; + +#endif From 94cc9439d1cf4feed758c14a7aa2223408cc66d9 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 24 Feb 2018 09:05:37 -0600 Subject: [PATCH 171/310] Addition of bootloaderExamine tool source --- tools/bootloaderExamine/main.cpp | 147 +++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 tools/bootloaderExamine/main.cpp diff --git a/tools/bootloaderExamine/main.cpp b/tools/bootloaderExamine/main.cpp new file mode 100644 index 00000000..aaf0f458 --- /dev/null +++ b/tools/bootloaderExamine/main.cpp @@ -0,0 +1,147 @@ +#include +#include + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t segment_count; + /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_mode; + /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_speed: 4; + /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint8_t spi_size: 4; + uint32_t entry_addr; + /* WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP + * pin and sets this field to 0xEE=disabled) */ + uint8_t wp_pin; + /* Drive settings for the SPI flash pins (read by ROM bootloader) */ + uint8_t spi_pin_drv[3]; + /* Reserved bytes in ESP32 additional header space, currently unused */ + uint8_t reserved[11]; + /* If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest + * is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature + * is appended after this (and the simple hash is included in the signed data). */ + uint8_t hash_appended; +} __attribute__((packed)) esp_image_header_t; + +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_segment_header_t; + + +/** + * @brief Return a memory area name given an address. + * @param addr The address to decode. + * @return A string description of the memory area. + */ +static const char* area(uint32_t addr) { + if (addr >= 0x3FF80000 && addr <= 0x3FF81FFF) { + return "RTS Fast (8K)"; + } + if (addr >= 0x3FF82000 && addr <= 0x3FF8FFFF) { + return "Reserved"; + } + if (addr >= 0x3FF90000 && addr <= 0x3FF9FFFF) { + return "Internal ROM 1 (64K)"; + } + if (addr >= 0x3FFA0000 && addr <= 0x3FFADFFF) { + return "Reserved"; + } + if (addr >= 0x3FFAE000 && addr <= 0x3FFDFFFF) { + return "Internal SRAM 2 (200K)"; + } + if (addr >= 0x3FFE0000 && addr <= 0x3FFFFFFF) { + return "Internal SRAM 1 (128K)"; + } + if (addr >= 0x40000000 && addr <= 0x40007FFF) { + return "Internal ROM 0 0 (32K)"; + } + if (addr >= 0x40008000 && addr <= 0x4005FFFF) { + return "Internal ROM 0 (352K)"; + } + if (addr >= 0x40060000 && addr <= 0x4006FFFF) { + return "Reserved"; + } + if (addr >= 0x40070000 && addr <= 0x4007FFFF) { + return "Internal SRAM 0 (64K)"; + } + if (addr >= 0x40080000 && addr <= 0x4009FFFF) { + return "Internal SRAM 0 (128K)"; + } + if (addr >= 0x400A0000 && addr <= 0x400AFFFF) { + return "Internal SRAM 1 (64K)"; + } + if (addr >= 0x400B0000 && addr <= 0x400B7FFF) { + return "Internal SRAM 1 (32K)"; + } + if (addr >= 0x400B8000 && addr <= 0x400BFFFF) { + return "Internal SRAM 1 (32K)"; + } + if (addr >= 0x400C0000 && addr <= 0x400C1FFF) { + return "RTC FAST Memory (8K)"; + } + if (addr >= 0x50000000 && addr <= 0x50001FFF) { + return "RTC SLOW Memory (8K)"; + } + if (addr >= 0x3F400000 && addr <= 0x3F7FFFFF) { + return "External Memory (Data)"; + } + if (addr >= 0x3F800000 && addr <= 0x3FBFFFFF) { + return "External Memory (Data)"; + } + if (addr >= 0x400C2000 && addr <= 0x40BFFFFF) { + return "External Memory (Instruction)"; + } + return "Un-described"; +} //area + + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ESP32_BIN_FILE.bin\n", argv[0]); + return 0; + } + + const char* fileName = argv[1]; + esp_image_header_t header; + esp_image_segment_header_t imageSegmentHeader; + + FILE* file = fopen(fileName, "rb"); + if (file == nullptr) { + printf("Failed to open file %s\n", fileName); + return 0; + } + + size_t count = fread(&header, sizeof(esp_image_header_t), 1, file); + if (count != 1) { + printf("Failed to read esp_image_header_t\n"); + return 0; + } + + printf("Dump of ESP32 binary file: %s\n", fileName); + printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s\n", + header.magic, header.segment_count, header.entry_addr, area(header.entry_addr)); + + printf("\n"); + printf("Seg | Start | End | Length | Area\n"); + printf("----+------------+------------+-------------------+-------------------------------\n"); + for (int i=0; i Date: Wed, 28 Feb 2018 11:08:23 +0100 Subject: [PATCH 172/310] Allow HttpParser to parse response messages as well as request messages --- cpp_utils/HttpParser.cpp | 52 ++++++++++++++++++++++++++++++++++++++++ cpp_utils/HttpParser.h | 6 +++++ 2 files changed, 58 insertions(+) diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index 9eb6d535..cc7b686b 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -168,6 +168,13 @@ std::string HttpParser::getVersion() { return m_version; } // getVersion +std::string HttpParser::getStatus() { + return m_status; +} // getStatus + +std::string HttpParser::getReason() { + return m_reason; +} // getReason /** * @brief Determine if we have a header of the given name. @@ -261,3 +268,48 @@ void HttpParser::parseRequestLine(std::string &line) { m_version = toCharToken(it, line, ' '); ESP_LOGD(LOG_TAG, "<< parseRequestLine: method: %s, url: %s, version: %s", m_method.c_str(), m_url.c_str(), m_version.c_str()); } // parseRequestLine + +/** + * @brief Parse a response message. + * @param [in] line The response to parse. + */ +// A response is built from: +// A status line, any number of header lines, a body +// +void HttpParser::parseResponse(std::string message) +{ + auto it = message.begin(); + auto line = toStringToken(it, message, lineTerminator); + parseStatusLine(line); + + line = toStringToken(it, message, lineTerminator); + while(!line.empty()) { + ESP_LOGD(LOG_TAG, "Header: \"%s\"", line.c_str()); + m_headers.insert(parseHeader(line)); + line = toStringToken(it, message, lineTerminator); + } + + m_body = message.substr(std::distance(message.begin(), it)); +} // parse + +/** + * @brief Parse A status line. + * @param [in] line The status line to parse. + */ +// A status Line is built from: +// +// +void HttpParser::parseStatusLine(std::string &line) +{ + ESP_LOGD(LOG_TAG, ">> ParseStatusLine: \"%s\" [%d]", line.c_str(), line.length()); + std::string::iterator it = line.begin(); + // Get the version + m_version = toCharToken(it, line, ' '); + // Get the version + m_status = toCharToken(it, line, ' '); + // Get the status code + m_reason = toStringToken(it, line, lineTerminator); + + ESP_LOGD(LOG_TAG, "<< ParseStatusLine: method: %s, version: %s, status: %s", m_method.c_str(), m_version.c_str(), m_status.c_str()); +} // parseRequestLine + diff --git a/cpp_utils/HttpParser.h b/cpp_utils/HttpParser.h index 77191163..47aafcea 100644 --- a/cpp_utils/HttpParser.h +++ b/cpp_utils/HttpParser.h @@ -17,9 +17,12 @@ class HttpParser { std::string m_url; std::string m_version; std::string m_body; + std::string m_status; + std::string m_reason; std::map m_headers; void dump(); void parseRequestLine(std::string &line); + void parseStatusLine(std::string &line); public: HttpParser(); virtual ~HttpParser(); @@ -29,9 +32,12 @@ class HttpParser { std::string getMethod(); std::string getURL(); std::string getVersion(); + std::string getStatus(); + std::string getReason(); bool hasHeader(const std::string& name); void parse(std::string message); void parse(Socket s); + void parseResponse(std::string message); }; #endif /* CPP_UTILS_HTTPPARSER_H_ */ From 280aa8469a3387e4c3b211809395ced0870f5a8d Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 2 Mar 2018 17:41:04 +0100 Subject: [PATCH 173/310] Retry if esp_wifi_connect fails to connect --- cpp_utils/WiFi.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index f3176530..5fdaede1 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -194,14 +194,17 @@ bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool } m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. + do { + ESP_LOGD(LOG_TAG, "esp_wifi_connect"); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s + m_connectFinished.give(); - errRc = ::esp_wifi_connect(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - m_connectFinished.wait("connectAP"); // Wait for the completion of the connection. ESP_LOGD(LOG_TAG, "<< connectAP"); return m_apConnected; // Return true if we are now connected and false if not. } // connectAP From f182335222617611ac1c24229993e47ace2a9cf9 Mon Sep 17 00:00:00 2001 From: "R. Main" Date: Sat, 3 Mar 2018 12:58:48 -0500 Subject: [PATCH 174/310] Update u8g2_esp32_hal.c To ensure use of GPIO32 & GPIO33 are supported (34-39 are input only), must define literal constant as 'unsigned long long', or the compiler produces code that fails to perform the required shift, resulting in corruption of the GPIO matrix. --- hardware/displays/U8G2/u8g2_esp32_hal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardware/displays/U8G2/u8g2_esp32_hal.c b/hardware/displays/U8G2/u8g2_esp32_hal.c index 05a2c994..fdab51a3 100644 --- a/hardware/displays/U8G2/u8g2_esp32_hal.c +++ b/hardware/displays/U8G2/u8g2_esp32_hal.c @@ -176,13 +176,13 @@ uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, case U8X8_MSG_GPIO_AND_DELAY_INIT: { uint64_t bitmask = 0; if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { - bitmask = bitmask | (1< Date: Sun, 4 Mar 2018 11:15:42 -0600 Subject: [PATCH 175/310] Addition of MMU tool --- tools/MMUTool/MMUTool.html | 134 +++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tools/MMUTool/MMUTool.html diff --git a/tools/MMUTool/MMUTool.html b/tools/MMUTool/MMUTool.html new file mode 100644 index 00000000..b50337ab --- /dev/null +++ b/tools/MMUTool/MMUTool.html @@ -0,0 +1,134 @@ + + + + +MMUTool + + + + + +

ESP32 MMU Tool

+

Storage address:

+

MMU entry:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
0x3F40 00000x3F40 FFFF0Data
0x3F41 00000x3F41 FFFF1Data
.........Data
0x3F7F 00000x3F7F FFFF63Data
0x4000 00000x4000 FFFF64Instruction
0x4001 00000x4001 FFFF65Instruction
.........Instruction
0x403F 00000x403F FFFF127Instruction
0x4040 00000x4040 FFFF128Instruction
.........Instruction
0x407F 00000x407F FFFF255Instruction
+ + + \ No newline at end of file From dbad017d0449d662e78a75b72c3700a5868163d3 Mon Sep 17 00:00:00 2001 From: Damon Smith Date: Mon, 5 Mar 2018 21:36:05 +1100 Subject: [PATCH 176/310] stop NVS get from crashing if the value hasn't been set yet, also expose a function in wifi to get connected state. --- cpp_utils/CPPNVS.cpp | 18 ++++++++++++------ cpp_utils/CPPNVS.h | 6 +++--- cpp_utils/WiFi.cpp | 9 +++++++++ cpp_utils/WiFi.h | 1 + 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index d0a2a0bb..f363439a 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -70,12 +70,16 @@ void NVS::erase(std::string key) { * @param [in] key The key to read from the namespace. * @param [out] result The string read from the %NVS storage. */ -void NVS::get(std::string key, std::string* result, bool isBlob) { +int NVS::get(std::string key, std::string* result, bool isBlob) { size_t length; if (isBlob) { - ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); + esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); + ESP_LOGD(LOG_TAG, "Error getting key: %i", rc); + return rc; } else { - ::nvs_get_str(m_handle, key.c_str(), NULL, &length); + esp_err_t rc = ::nvs_get_str(m_handle, key.c_str(), NULL, &length); + ESP_LOGD(LOG_TAG, "Error getting key: %i", rc); + return rc; } char *data = (char *)malloc(length); if (isBlob) { @@ -85,21 +89,23 @@ void NVS::get(std::string key, std::string* result, bool isBlob) { } *result = std::string(data); free(data); + return ESP_OK; } // get -void NVS::get(std::string key, uint32_t& value) { - ::nvs_get_u32(m_handle, key.c_str(), &value); +int NVS::get(std::string key, uint32_t& value) { + return ::nvs_get_u32(m_handle, key.c_str(), &value); } // get - uint32_t -void NVS::get(std::string key, uint8_t* result, size_t& length) { +int NVS::get(std::string key, uint8_t* result, size_t& length) { ESP_LOGD(LOG_TAG, ">> get: key: %s, blob: inputSize: %d", key.c_str(), length); esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), result, &length); if (rc != ESP_OK) { ESP_LOGD(LOG_TAG, "nvs_get_blob: %d", rc); } ESP_LOGD(LOG_TAG, "<< get: outputSize: %d", length); + return rc; } // get - blob diff --git a/cpp_utils/CPPNVS.h b/cpp_utils/CPPNVS.h index 755f551b..6afbc166 100644 --- a/cpp_utils/CPPNVS.h +++ b/cpp_utils/CPPNVS.h @@ -21,9 +21,9 @@ class NVS { void erase(); void erase(std::string key); - void get(std::string key, std::string* result, bool isBlob=false); - void get(std::string key, uint8_t* result, size_t &length); - void get(std::string key, uint32_t& value); + int get(std::string key, std::string* result, bool isBlob=false); + int get(std::string key, uint8_t* result, size_t &length); + int get(std::string key, uint32_t& value); void set(std::string key, std::string data, bool isBlob=false); void set(std::string key, uint32_t value); void set(std::string key, uint8_t* data, size_t length); diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 5fdaede1..bfe9477d 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -224,6 +224,15 @@ void WiFi::dump() { } // dump +/** + * @brief Returns whether wifi is connected to an access point + */ +bool WiFi::isConnectedToAP() { + return m_apConnected; +} // isConnected + + + /** * @brief Primary event handler interface. */ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index b06635d9..e12994ef 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -132,6 +132,7 @@ class WiFi { struct in_addr getHostByName(const char* hostName); bool connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); void dump(); + bool isConnectedToAP(); static std::string getApMac(); static tcpip_adapter_ip_info_t getApIpInfo(); static std::string getApSSID(); From 3436c8735202bc6f7a479772df635e76d3d29c4e Mon Sep 17 00:00:00 2001 From: pcbreflux Date: Tue, 6 Mar 2018 13:07:22 +0100 Subject: [PATCH 177/310] BLE_iBeacon --- .../Arduino/BLE_iBeacon/BLE_iBeacon.ino | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 00000000..5f6ed002 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,104 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by pcbreflux +*/ + + +/* + Create a BLE server that will send periodic iBeacon frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ +#include "sys/time.h" + +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLEBeacon.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t temprature_sens_read(); +//uint8_t g_phyFuns; + +#ifdef __cplusplus +} +#endif + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +void setBeacon() { + + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); + oBeacon.setMinor(bootcount&0xFFFF); + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 + + std::string strServiceData = ""; + + strServiceData += (char)26; // Len + strServiceData += (char)0xFF; // Type + strServiceData += oBeacon.getData(); + oAdvertisementData.addData(strServiceData); + + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); + +} + +void setup() { + + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n",bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init(""); + + // Create the BLE Server + BLEServer *pServer = BLEDevice::createServer(); + + pAdvertising = pServer->getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(100); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() { +} From b1c5399325fdd3065b11c0715bdad63e1c3d4a4c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 10 Mar 2018 18:41:21 -0600 Subject: [PATCH 178/310] Change for this forum post: https://esp32.com/viewtopic.php?f=2&t=3756 --- .../paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c b/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c index f46a0fde..38de27c9 100644 --- a/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c +++ b/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c @@ -215,7 +215,9 @@ static int NetworkConnectSSL(Network *n, char *addr, int port) { mbedtls_entropy_init(&mqtt_ssl_context->entropy); mbedtls_ssl_conf_dbg(&mqtt_ssl_context->conf, my_debug, stdout); +#ifdef CONFIG_MBEDTLS_DEBUG mbedtls_debug_set_threshold(4); // Log at verbose only +#endif // CONFIG_MBEDTLS_DEBUG int ret = mbedtls_ctr_drbg_seed( &mqtt_ssl_context->ctr_drbg, From aef10f41f339be05d2e7e3b5fb1b2b7bcf80adf1 Mon Sep 17 00:00:00 2001 From: Damon Smith Date: Tue, 13 Mar 2018 00:12:11 +1100 Subject: [PATCH 179/310] only return NVS error code if it is not OK --- cpp_utils/CPPNVS.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index f363439a..b204045b 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -74,12 +74,16 @@ int NVS::get(std::string key, std::string* result, bool isBlob) { size_t length; if (isBlob) { esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); - ESP_LOGD(LOG_TAG, "Error getting key: %i", rc); - return rc; + if (rc != ESP_OK) { + ESP_LOGI(LOG_TAG, "Error getting key: %i", rc); + return rc; + } } else { esp_err_t rc = ::nvs_get_str(m_handle, key.c_str(), NULL, &length); - ESP_LOGD(LOG_TAG, "Error getting key: %i", rc); - return rc; + if (rc != ESP_OK) { + ESP_LOGI(LOG_TAG, "Error getting key: %i", rc); + return rc; + } } char *data = (char *)malloc(length); if (isBlob) { From 5c893018ba951c3025f492130ce38296fbba7fe0 Mon Sep 17 00:00:00 2001 From: pcbreflux Date: Tue, 13 Mar 2018 09:27:34 +0100 Subject: [PATCH 180/310] BLEEddystone --- cpp_utils/BLEEddystoneTLM.cpp | 134 ++++++++++++++++++++++++++++++ cpp_utils/BLEEddystoneTLM.h | 50 ++++++++++++ cpp_utils/BLEEddystoneURL.cpp | 149 ++++++++++++++++++++++++++++++++++ cpp_utils/BLEEddystoneURL.h | 42 ++++++++++ 4 files changed, 375 insertions(+) create mode 100644 cpp_utils/BLEEddystoneTLM.cpp create mode 100644 cpp_utils/BLEEddystoneTLM.h create mode 100644 cpp_utils/BLEEddystoneURL.cpp create mode 100644 cpp_utils/BLEEddystoneURL.h diff --git a/cpp_utils/BLEEddystoneTLM.cpp b/cpp_utils/BLEEddystoneTLM.cpp new file mode 100644 index 00000000..0c61ac1b --- /dev/null +++ b/cpp_utils/BLEEddystoneTLM.cpp @@ -0,0 +1,134 @@ +/* + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "Arduino.h" +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEEddystoneTLM.h" + +static const char LOG_TAG[] = "BLEEddystoneTLM"; +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) + +BLEEddystoneTLM::BLEEddystoneTLM() { + beconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t)((float)23.00); + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; +} // BLEEddystoneTLM + +std::string BLEEddystoneTLM::getData() { + return std::string((char*)&m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +BLEUUID BLEEddystoneTLM::getUUID() { + return BLEUUID(beconUUID); +} // getUUID + +uint8_t BLEEddystoneTLM::getVersion() { + return m_eddystoneData.version; +} // getVersion + +uint16_t BLEEddystoneTLM::getVolt() { + return m_eddystoneData.volt; +} // getVolt + +float BLEEddystoneTLM::getTemp() { + return (float)m_eddystoneData.temp; +} // getTemp + +uint32_t BLEEddystoneTLM::getCount() { + return m_eddystoneData.advCount; +} // getCount + +uint32_t BLEEddystoneTLM::getTime() { + return m_eddystoneData.tmil; +} // getTime + +std::string BLEEddystoneTLM::toString() { + std::string out = ""; + String buff; + uint32_t rawsec; + + out += "Version "; + buff = String(m_eddystoneData.version, DEC); + out += buff.c_str(); + out += "\n"; + + out += "Battery Voltage "; + buff = String(ENDIAN_CHANGE_U16(m_eddystoneData.volt), DEC); + out += buff.c_str(); + out += " mV\n"; + + out += "Temperature "; + buff = String((float)m_eddystoneData.temp, 1); + out += buff.c_str(); + out += " °C\n"; + + out += "Adv. Count "; + buff = String(ENDIAN_CHANGE_U32(m_eddystoneData.advCount), DEC); + out += buff.c_str(); + out += "\n"; + + out += "Time "; + rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + buff = "0000"+String(rawsec/864000, DEC); + out += buff.substring(buff.length()-4,buff.length()).c_str(); + out += "."; + buff = "00"+String((rawsec/36000)%24, DEC); + out += buff.substring(buff.length()-2,buff.length()).c_str(); + out += ":"; + buff = "00"+String((rawsec/600)%60, DEC); + out += buff.substring(buff.length()-2,buff.length()).c_str(); + out += ":"; + buff = "00"+String((rawsec/10)%60, DEC); + out += buff.substring(buff.length()-2,buff.length()).c_str(); + out += "\n"; + + return out; +} // toString + +/** + * Set the raw data for the beacon record. + */ +void BLEEddystoneTLM::setData(std::string data) { + if (data.length() != sizeof(m_eddystoneData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data.data(), data.length()); +} // setData + +void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) { + beconUUID = l_uuid.getNative()->uuid.uuid16; +} // setUUID + +void BLEEddystoneTLM::setVersion(uint8_t version) { + m_eddystoneData.version = version; +} // setVersion + +void BLEEddystoneTLM::setVolt(uint16_t volt) { + m_eddystoneData.volt = volt; +} // setVolt + +void BLEEddystoneTLM::setTemp(float temp) { + m_eddystoneData.temp = (uint16_t)temp; +} // setTemp + +void BLEEddystoneTLM::setCount(uint32_t advCount) { + m_eddystoneData.advCount = advCount; +} // setCount + +void BLEEddystoneTLM::setTime(uint32_t tmil) { + m_eddystoneData.tmil = tmil; +} // setTime + +#endif diff --git a/cpp_utils/BLEEddystoneTLM.h b/cpp_utils/BLEEddystoneTLM.h new file mode 100644 index 00000000..76bd6a43 --- /dev/null +++ b/cpp_utils/BLEEddystoneTLM.h @@ -0,0 +1,50 @@ +/* + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _BLEEddystoneTLM_H_ +#define _BLEEddystoneTLM_H_ +#include "BLEUUID.h" + +#define EDDYSTONE_TLM_FRAME_TYPE 0x20 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class BLEEddystoneTLM { +private: + uint16_t beconUUID; + struct { + uint8_t frameType; + int8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed))m_eddystoneData; +public: + BLEEddystoneTLM(); + std::string getData(); + BLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + +}; // BLEEddystoneTLM + +#endif /* _BLEEddystoneTLM_H_ */ diff --git a/cpp_utils/BLEEddystoneURL.cpp b/cpp_utils/BLEEddystoneURL.cpp new file mode 100644 index 00000000..6c12b246 --- /dev/null +++ b/cpp_utils/BLEEddystoneURL.cpp @@ -0,0 +1,149 @@ +/* + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEEddystoneURL.h" + +static const char LOG_TAG[] = "BLEEddystoneURL"; + +BLEEddystoneURL::BLEEddystoneURL() { + beconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); +} // BLEEddystoneURL + +std::string BLEEddystoneURL::getData() { + return std::string((char*)&m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +BLEUUID BLEEddystoneURL::getUUID() { + return BLEUUID(beconUUID); +} // getUUID + +int8_t BLEEddystoneURL::getPower() { + return m_eddystoneData.advertisedTxPower; +} // getPower + +std::string BLEEddystoneURL::getURL() { + return std::string((char*)&m_eddystoneData.url, sizeof(m_eddystoneData.url)); +} // getURL + +std::string BLEEddystoneURL::getDecodedURL() { + std::string decodedURL = ""; + + switch (m_eddystoneData.url[0]) { + case 0x00: + decodedURL += "http://www."; + break; + case 0x01: + decodedURL += "https://www."; + break; + case 0x02: + decodedURL += "http://"; + break; + case 0x03: + decodedURL += "https://"; + break; + default: + decodedURL += m_eddystoneData.url[0]; + } + + for (int i=1;i33&&m_eddystoneData.url[i]<127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + } + } + } + + + return decodedURL; +} // getDecodedURL + + + +/** + * Set the raw data for the beacon record. + */ +void BLEEddystoneURL::setData(std::string data) { + if (data.length() > sizeof(m_eddystoneData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData)); + return; + } + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memcpy(&m_eddystoneData, data.data(), data.length()); + lengthURL=data.length()-(sizeof(m_eddystoneData)-sizeof(m_eddystoneData.url)); + +} // setData + +void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { + beconUUID = l_uuid.getNative()->uuid.uuid16; +} // setUUID + +void BLEEddystoneURL::setPower(int8_t advertisedTxPower) { + m_eddystoneData.advertisedTxPower = advertisedTxPower; +} // setPower + +void BLEEddystoneURL::setURL(std::string url) { + if (url.length() > sizeof(m_eddystoneData.url)) { + ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url)); + return; + } + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + memcpy(m_eddystoneData.url, url.data(), url.length()); + lengthURL=url.length(); +} // setURL + + +#endif diff --git a/cpp_utils/BLEEddystoneURL.h b/cpp_utils/BLEEddystoneURL.h new file mode 100644 index 00000000..2025cb19 --- /dev/null +++ b/cpp_utils/BLEEddystoneURL.h @@ -0,0 +1,42 @@ +/* + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _BLEEddystoneURL_H_ +#define _BLEEddystoneURL_H_ +#include "BLEUUID.h" + +#define EDDYSTONE_URL_FRAME_TYPE 0x10 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class BLEEddystoneURL { +private: + uint16_t beconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed))m_eddystoneData; +public: + BLEEddystoneURL(); + std::string getData(); + BLEUUID getUUID(); + int8_t getPower(); + std::string getURL(); + std::string getDecodedURL(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(std::string url); + +}; // BLEEddystoneURL + +#endif /* _BLEEddystoneURL_H_ */ From 247d66da92827c9bd6a6f586dd70f8cef6eec5e0 Mon Sep 17 00:00:00 2001 From: Thomas van de Wege Date: Sat, 17 Mar 2018 14:45:38 +0100 Subject: [PATCH 181/310] Replaced error message for double characteristics Made the fix that @chegewara proposed. I was confused by the error and found this github issue in the repository: https://github.com/nkolban/esp32-snippets/issues/175 --- cpp_utils/BLEService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 32bcc570..64f60a9a 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -192,7 +192,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // Check that we don't add the same characteristic twice. if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { - ESP_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID"); + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); //return; } From 8d3b0f93dded47e2f8b9f8e02e5c9376bf28082a Mon Sep 17 00:00:00 2001 From: mws-rmain <30533684+mws-rmain@users.noreply.github.com> Date: Mon, 19 Mar 2018 13:59:26 -0400 Subject: [PATCH 182/310] Update BLEDevice.cpp Fixes to allow BLEDevice to compile with security disabled (CONFIG_BLE_SMP_ENABLE undefined) --- cpp_utils/BLEDevice.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index b333ed7c..f686f1a5 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -99,9 +99,11 @@ uint16_t BLEDevice::m_localMTU = 23; switch(event) { case ESP_GATTS_CONNECT_EVT: { BLEDevice::m_localMTU = 23; +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } +#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT @@ -148,9 +150,11 @@ uint16_t BLEDevice::m_localMTU = 23; ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } +#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTC_CONNECT_EVT @@ -190,16 +194,20 @@ uint16_t BLEDevice::m_localMTU = 23; break; case ESP_GAP_BLE_NC_REQ_EVT: ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityCallbacks!=nullptr){ esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); } +#endif // CONFIG_BLE_SMP_ENABLE break; case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityCallbacks!=nullptr){ esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); } +#endif // CONFIG_BLE_SMP_ENABLE break; /* * TODO should we add white/black list comparison? @@ -208,12 +216,14 @@ uint16_t BLEDevice::m_localMTU = 23; /* send the positive(true) security response to the peer device to accept the security request. If not accept the security request, should sent the security response with negative(false) accept value*/ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityCallbacks!=nullptr){ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); } else{ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); } +#endif // CONFIG_BLE_SMP_ENABLE break; /* * @@ -221,19 +231,27 @@ uint16_t BLEDevice::m_localMTU = 23; case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. ///show the passkey number to the user to input it in the peer deivce. ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityCallbacks!=nullptr){ ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); } +#endif // CONFIG_BLE_SMP_ENABLE break; case ESP_GAP_BLE_KEY_EVT: //shows the ble key type info share with peer device to the user. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); +#endif // CONFIG_BLE_SMP_ENABLE break; case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if(BLEDevice::m_securityCallbacks!=nullptr){ BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); } +#endif // CONFIG_BLE_SMP_ENABLE break; default: { break; @@ -382,12 +400,14 @@ uint16_t BLEDevice::m_localMTU = 23; return; }; +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; }; +#endif // CONFIG_BLE_SMP_ENABLE } vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. } // init From 6a9c83f7a9b640eb83e6fc85ae8ba3d9c86b556e Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 22 Mar 2018 07:16:44 +0100 Subject: [PATCH 183/310] Fix gatts_add_char_descr_evt_param stuct refactored --- cpp_utils/BLEDescriptor.cpp | 2 +- cpp_utils/BLEUtils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 1a72ef30..58ff78b4 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -155,7 +155,7 @@ void BLEDescriptor::handleGATTServerEvent( (uint32_t)m_pCharacteristic->getService()->getLastCreatedCharacteristic()); */ if (m_pCharacteristic != nullptr && - m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid)) && + m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) && m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { setHandle(param->add_char_descr.attr_handle); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index ff4ebfaf..a33ee27a 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1654,7 +1654,7 @@ void BLEUtils::dumpGattServerEvent( evtParam->add_char_descr.attr_handle, evtParam->add_char_descr.service_handle, evtParam->add_char_descr.service_handle, - BLEUUID(evtParam->add_char_descr.char_uuid).toString().c_str()); + BLEUUID(evtParam->add_char_descr.descr_uuid).toString().c_str()); break; } // ESP_GATTS_ADD_CHAR_DESCR_EVT From 7500a986afab3e662a51f8d567aa5d62a8d35bdf Mon Sep 17 00:00:00 2001 From: Thomas van de Wege Date: Thu, 22 Mar 2018 19:23:58 +0100 Subject: [PATCH 184/310] Adding getter for the initialized variable --- cpp_utils/BLEDevice.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 7f331435..7a1b833d 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -42,6 +42,7 @@ class BLEDevice { static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); + static bool getInitialized(); // Returns the state of the device, is it initialized or not? private: static BLEServer *m_pServer; From a8f98eaaac1867f4ddd0f6d8c83c0b304572ccda Mon Sep 17 00:00:00 2001 From: Thomas van de Wege Date: Thu, 22 Mar 2018 19:26:38 +0100 Subject: [PATCH 185/310] Added getter for the initialized variable --- cpp_utils/BLEDevice.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index f686f1a5..1c6e6bb8 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -527,4 +527,8 @@ esp_err_t BLEDevice::setMTU(uint16_t mtu) { uint16_t BLEDevice::getMTU() { return m_localMTU; } + +bool BLEDevice::getInitialized() { + return initialized; +} #endif // CONFIG_BT_ENABLED From 32729aeac9c8eafcf022826a63908543cbc2fe5c Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 28 Mar 2018 00:49:20 +0200 Subject: [PATCH 186/310] Fix Compiler option->Optimization level->Debug error --- cpp_utils/BLEService.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 64f60a9a..4e59c4a4 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -72,6 +72,7 @@ void BLEService::executeCreate(BLEServer *pServer) { m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT esp_gatt_srvc_id_t srvc_id; + srvc_id.is_primary = true; srvc_id.id.inst_id = 0; srvc_id.id.uuid = *m_uuid.getNative(); esp_err_t errRc = ::esp_ble_gatts_create_service( From 17cd0532f74ff46dc5272e1d09016edad34713bf Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 29 Mar 2018 18:46:10 -0500 Subject: [PATCH 187/310] Moved BLEEddystonefiles to onhold as per #447 --- .cproject | 100 ++++++++++++++++++++- cpp_utils/I2C.cpp | 2 +- cpp_utils/{ => onhold}/BLEEddystoneTLM.cpp | 0 cpp_utils/{ => onhold}/BLEEddystoneTLM.h | 0 cpp_utils/{ => onhold}/BLEEddystoneURL.cpp | 0 cpp_utils/{ => onhold}/BLEEddystoneURL.h | 0 tools/bootloaderExamine/main.cpp | 4 +- 7 files changed, 99 insertions(+), 7 deletions(-) rename cpp_utils/{ => onhold}/BLEEddystoneTLM.cpp (100%) rename cpp_utils/{ => onhold}/BLEEddystoneTLM.h (100%) rename cpp_utils/{ => onhold}/BLEEddystoneURL.cpp (100%) rename cpp_utils/{ => onhold}/BLEEddystoneURL.h (100%) diff --git a/.cproject b/.cproject index 379a8d68..54db1eb5 100644 --- a/.cproject +++ b/.cproject @@ -26,15 +26,46 @@ + + + diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 7c8f5056..96618bc2 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -69,7 +69,7 @@ void I2C::endTransaction() { errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000/portTICK_PERIOD_MS); if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + ESP_LOGE(LOG_TAG, "i2c_master_cmd_begin: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } ::i2c_cmd_link_delete(m_cmd); m_directionKnown = false; diff --git a/cpp_utils/BLEEddystoneTLM.cpp b/cpp_utils/onhold/BLEEddystoneTLM.cpp similarity index 100% rename from cpp_utils/BLEEddystoneTLM.cpp rename to cpp_utils/onhold/BLEEddystoneTLM.cpp diff --git a/cpp_utils/BLEEddystoneTLM.h b/cpp_utils/onhold/BLEEddystoneTLM.h similarity index 100% rename from cpp_utils/BLEEddystoneTLM.h rename to cpp_utils/onhold/BLEEddystoneTLM.h diff --git a/cpp_utils/BLEEddystoneURL.cpp b/cpp_utils/onhold/BLEEddystoneURL.cpp similarity index 100% rename from cpp_utils/BLEEddystoneURL.cpp rename to cpp_utils/onhold/BLEEddystoneURL.cpp diff --git a/cpp_utils/BLEEddystoneURL.h b/cpp_utils/onhold/BLEEddystoneURL.h similarity index 100% rename from cpp_utils/BLEEddystoneURL.h rename to cpp_utils/onhold/BLEEddystoneURL.h diff --git a/tools/bootloaderExamine/main.cpp b/tools/bootloaderExamine/main.cpp index aaf0f458..22e8a5aa 100644 --- a/tools/bootloaderExamine/main.cpp +++ b/tools/bootloaderExamine/main.cpp @@ -121,8 +121,8 @@ int main(int argc, char *argv[]) { } printf("Dump of ESP32 binary file: %s\n", fileName); - printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s\n", - header.magic, header.segment_count, header.entry_addr, area(header.entry_addr)); + printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s, hash_appended: %d\n", + header.magic, header.segment_count, header.entry_addr, area(header.entry_addr), header.hash_appended); printf("\n"); printf("Seg | Start | End | Length | Area\n"); From ab08a657d380460e6389e592dde963780a323080 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 29 Mar 2018 23:41:23 -0500 Subject: [PATCH 188/310] Check for member not present #454 --- cpp_utils/JSON.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 2c1de785..c55f3a78 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -229,6 +229,9 @@ JsonArray JsonObject::getArray(std::string name) { */ bool JsonObject::getBoolean(std::string name) { cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) { + return false; + } return cJSON_IsTrue(node); } // getBoolean @@ -240,6 +243,9 @@ bool JsonObject::getBoolean(std::string name) { */ double JsonObject::getDouble(std::string name) { cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) { + return 0.0; + } return node->valuedouble; } // getDouble @@ -251,6 +257,9 @@ double JsonObject::getDouble(std::string name) { */ int JsonObject::getInt(std::string name) { cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) { + return 0; + } return node->valueint; } // getInt @@ -269,10 +278,13 @@ JsonObject JsonObject::getObject(std::string name) { /** * @brief Get the named string value from the object. * @param [in] name The name of the object property. - * @return The string value from the object. + * @return The string value from the object. A zero length string is returned when the object is not present. */ std::string JsonObject::getString(std::string name) { cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) { + return ""; + } return std::string(node->valuestring); } // getString From 054a87b1731a4404c06b9839fbf31a104493d25c Mon Sep 17 00:00:00 2001 From: Testato Date: Mon, 2 Apr 2018 08:31:18 +0200 Subject: [PATCH 189/310] Update espToError.c --- error handling/fragments/espToError.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/error handling/fragments/espToError.c b/error handling/fragments/espToError.c index 66e90eb9..72127583 100644 --- a/error handling/fragments/espToError.c +++ b/error handling/fragments/espToError.c @@ -13,7 +13,7 @@ char *espToString(esp_err_t value) { case ESP_ERR_INVALID_ARG: return "Invalid argument"; case ESP_ERR_INVALID_SIZE: - return "Invalid state"; + return "Invalid size"; case ESP_ERR_INVALID_STATE: return "Invalid state"; case ESP_ERR_NOT_FOUND: From 9c796bbe0038d65e6ae2b19a8bc2791497a04d8c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 3 Apr 2018 10:31:38 -0500 Subject: [PATCH 190/310] Changes for esp_ble_gattc_open in ESP-IDF --- cpp_utils/BLEClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 55af054c..57ff4d21 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -110,6 +110,7 @@ bool BLEClient::connect(BLEAddress address) { errRc = ::esp_ble_gattc_open( getGattcIf(), *getPeerAddress().getNative(), // address + BLE_ADDR_TYPE_PUBLIC, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. 1 // direct connection ); if (errRc != ESP_OK) { From f42b82430de669ffbc6f6bf930339cecda60985b Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 6 Apr 2018 15:09:36 -0500 Subject: [PATCH 191/310] Made addData and getPayload public --- cpp_utils/BLEAdvertising.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index d1fa3c73..e3165298 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -29,13 +29,12 @@ class BLEAdvertisementData { void setPartialServices(BLEUUID uuid); void setServiceData(BLEUUID uuid, std::string data); void setShortName(std::string name); + void addData(std::string data); // Add data to the payload. + std::string getPayload(); // Retrieve the current advert payload. private: friend class BLEAdvertising; std::string m_payload; // The payload of the advertisement. - - void addData(std::string data); // Add data to the payload. - std::string getPayload(); // Retrieve the current advert payload. }; // BLEAdvertisementData From 7aaed6d3355bd0b251bc9386b742ee0699ee5a71 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 6 Apr 2018 16:09:54 -0500 Subject: [PATCH 192/310] Fixes on BLE_Notify --- cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino index 57ad7a7d..5e915bea 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino @@ -23,7 +23,8 @@ #include #include -BLEServer *pServer = NULL; +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; bool deviceConnected = false; bool oldDeviceConnected = false; uint8_t value = 0; @@ -61,7 +62,7 @@ void setup() { BLEService *pService = pServer->createService(SERVICE_UUID); // Create a BLE Characteristic - BLECharacteristic * pCharacteristic = pService->createCharacteristic( + pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | From bbec0b9793dce4bb8ca89c529aa7d86428af58b6 Mon Sep 17 00:00:00 2001 From: jrickard Date: Sat, 7 Apr 2018 14:10:04 -0500 Subject: [PATCH 193/310] BLECharacterstic.setValue() overrides to allow additional data types Revised BLECharacterstic.setValue() to allow additional data types when setting characterstic values. Added for uint16_t, uint32_t, int, float, and double. These data types appear to carry through to iPhone examination of ESP32 BLE transmission. --- cpp_utils/BLECharacteristic.cpp | 37 +++++++++++++++++++++++++++++++++ cpp_utils/BLECharacteristic.h | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 5e5aa2ac..931c753d 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -683,6 +683,43 @@ void BLECharacteristic::setValue(std::string value) { setValue((uint8_t*)(value.data()), value.length()); } // setValue +void BLECharacteristic::setValue(uint16_t& data16) { + uint8_t temp[2]; + temp[0]=data16; + temp[1]=data16>>8; + setValue(temp, 2); +} // setValue + +void BLECharacteristic::setValue(uint32_t& data32) { + uint8_t temp[4]; + temp[0]=data32; + temp[1]=data32>>8; + temp[2]=data32>>16; + temp[3]=data32>>24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(int& data32) { + uint8_t temp[4]; + temp[0]=data32; + temp[1]=data32>>8; + temp[2]=data32>>16; + temp[3]=data32>>24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(float& data32) { + uint8_t temp[4]; + *((float *)temp) = data32; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(double& data64) { + uint8_t temp[8]; + *((double *)temp) = data64; + setValue(temp, 8); +} // setValue + /** * @brief Set the Write No Response property value. diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 10dc787f..b3f8d2e9 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -75,6 +75,11 @@ class BLECharacteristic { void setReadProperty(bool value); void setValue(uint8_t* data, size_t size); void setValue(std::string value); + void setValue(uint16_t& data16); + void setValue(uint32_t& data32); + void setValue(int& data32); + void setValue(float& data32); + void setValue(double& data64); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); std::string toString(); From 6fa380ba2d9924943c796d9a3780d51cd917943c Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 9 Apr 2018 22:23:15 -0500 Subject: [PATCH 194/310] Addition of esptool libs --- tools/esptool_libs/README.md | 4 + tools/esptool_libs/nodejs/espjs.js | 1029 ++++++++++++++++++++++++++++ 2 files changed, 1033 insertions(+) create mode 100644 tools/esptool_libs/README.md create mode 100644 tools/esptool_libs/nodejs/espjs.js diff --git a/tools/esptool_libs/README.md b/tools/esptool_libs/README.md new file mode 100644 index 00000000..b337ac56 --- /dev/null +++ b/tools/esptool_libs/README.md @@ -0,0 +1,4 @@ +# Esptool libraries +The Espressif awesome esptool performs just about anything you may want to do relating to ESP32 programming over UART. It can write to flash, write to memory, erase flash and a world of other features. The specification of the protocol can be found in the `esptool` github project Wiki. + +This section of this repository hosts libraries for working with this protocol. There is an implementation in Node.js and more to come including C and C++. \ No newline at end of file diff --git a/tools/esptool_libs/nodejs/espjs.js b/tools/esptool_libs/nodejs/espjs.js new file mode 100644 index 00000000..5285e265 --- /dev/null +++ b/tools/esptool_libs/nodejs/espjs.js @@ -0,0 +1,1029 @@ +/** + * Requires: + * * hexy + * * serialport - https://www.npmjs.com/package/serialport + * + * To debug, run: + * node --inspect-brk espjs.js + * + * In chrome run, + * + * chrome://inspect + * + */ + + /** + * We want to provide processing to both transmitted requests and received responses. A request is a message that is then encapsulated + * in a SLIP packet and transmitted via UART. A response is a message received via UART that is wrapped in a SLIP packet. + * + * We will logically define a DataPacket as the raw packet unit that is either transmitted or received. This encapsulates a + * Buffer that contains the data. + */ +const hexy = require("hexy"); +const zlib = require('zlib'); +const fs = require("fs"); +const assert = require("assert"); +const PORT = "/dev/ttyUSB1"; +const argv = require("minimist")(process.argv.slice(2)); + +const COMMAND_FLASH_BEGIN = 0x02; +const COMMAND_FLASH_DATA = 0x03; +const COMMAND_FLASH_END = 0x04; +const COMMAND_MEM_BEGIN = 0x05; +const COMMAND_MEM_END = 0x06; +const COMMAND_MEM_DATA = 0x07; +const COMMAND_SYNC = 0x08; +const COMMAND_WRITE_REG = 0x09; +const COMMAND_READ_REG = 0x0a; +const COMMAND_SPI_SET_PARAMS = 0x0b; +const COMMAND_SPI_FLASH_MD5 = 0x13; + +const EFUSE_REG_BASE = 0x6001a000; + +var esp32r0Delay = false; +var response = null; +var serialData = SerialData(); // The buffer holding the received serial data. + +var SerialPort = require("serialport"); +var port = new SerialPort(PORT, { + baudRate: 115200, + autoOpen: false +}); + +/** + * Maintain received serial data. + * We are receiving serial data asynchronosly from the serial processor. This means that when data arrives + * on the UART, it is passed to an instance of this object for accumulation. The data is passed in through a + * call to append. + */ +function SerialData() { + var serialDataBuffer; // The serial data we are accumulating. + + function empty() { + serialDataBuffer = Buffer.allocUnsafe(0); + } + + var resolveMoreData = null; + /** + * Append data received from the serial port into our buffer. + * @param {Buffer} data The data received from our serial port. + * @returns Nothing. + */ + function append(data) { + console.log("SerialData.append:\n%s", hexy.hexy(data)); + serialDataBuffer = Buffer.concat([serialDataBuffer, data]); // Append the new data into the serial buffer. + if (resolveMoreData != null) { // If we have a promise resolution that is waiting for more data .. + resolveMoreData(); // resolve the promise. + resolveModeData = null; + } + } // append + + /** + * Create a promise that is resolved when we have more data. + * @returns A promise that is resolved when we have more data. + */ + async function moreData() { + return new Promise((resolveA, rejectA) => { + resolveMoreData = resolveA; // The promise is resolved when append is called to provide more data. + }); + } // moreData + + + /** + * Assume that the serial data received is a sequence of packets. This function returns a promise that + * is resolved when a packet is available for processing. + */ + async function getNextDataPacket() { + var p = new Promise(async (resolve, reject) => { + console.log("getNextDataPacket>>"); + while(true) { // Keep looping until we have a data packet. + if (DataPacket().containsSLIPPacket(serialDataBuffer)) { // Does the buffer now contain a SLIP packet? + var dp = DataPacket(); // Create a new data packet + serialDataBuffer = dp.setSLIPPacket(serialDataBuffer); // Load the data packet with the raw serial data. + console.log("Got a new data packet: %s\n%s", dp.toString(), hexy.hexy(dp.getData())); + resolve(dp); + return; + } + await moreData(); // We didn't find a data packet in our data, so wait for more data. + } // We will loop forever waiting for packets. + }); + return p; // Return the promise that is resolved when we have a data packet. + } // getNextDataPacket + + empty(); // Initialize / empty the serial data. + return { + "append": append, + "empty": empty, + "getNextDataPacket": getNextDataPacket + } +} // SerialData + + +/** + * A data packet encapsulates a command or response. + */ +function DataPacket() { + var dataPacket = Buffer.allocUnsafe(0); + /** + * Examine the data buffer looking for a slip trailer (0xc0). If found, we return the index + * at which it was found. If not found, we return -1. + * @param {Buffer} data The data in which we are looking for the slip trailer. + * @returns The original of the slip trailer or -1 if not found. + */ + function getSLIPTrailer(data) { + for (i=1; i 0; + } // containsSLIPPacket + + + /** + * Process the data passed in that is SLIP encoded and build a parsed data packet. + * @param {Buffer} data The data to build a packet from. This will be raw data received from UART. + * @returns The unprocessed data. + */ + function setSLIPPacket(data) { + console.log("setSLIPPacket:\n%s", hexy.hexy(data)); + assert(data); + assert(data[0] == 0xc0); + assert(data.length > 0); + var end = getSLIPTrailer(data); + if (end == -1) { + throw "No SLIP trailer in data we are to process."; + } + assert(end > 0); + dataPacket = Buffer.allocUnsafe(data.length); // Our data will be no MORE than data.length bytes in size. + for (var i=1, j=0; i 0xc0 + dataPacket[j] = 0xc0; + i++ + } else if (data[i] == 0xdb && data[i+1] == 0xdd) { // 0xdb 0xdd -> 0xdb + dataPacket[j] = 0xdb; + i++; + } else { + dataPacket[j] = data[i] + } + } // End loop over data. + + // We now have a buffer (dataPacket) that contains our decoded data but will be too large. However, j contains the number of bytes we want. + var temp = Buffer.allocUnsafe(j); + dataPacket.copy(temp, 0, 0, j); // Copy dataPacket[0..j-1] -> temp + dataPacket = temp; + + // We have now processed what we wanted from the incoming data and now we must return the unused data. + i++; + var unprocessedLength = data.length - i; + temp = Buffer.allocUnsafe(unprocessedLength); + data.copy(temp, 0, i); // copy data[i..] -> temp + + console.log("setSLIPPacket: New data has length: %d, returning unused buffer of size: %d", dataPacket.length, temp.length); + console.log("Unused:\n%s", hexy.hexy(temp)); + return temp; + } // setSLIPPacket + + + /** + * Internally we have a dataPacket. We want to return a SLIP encoded buffer from that packet. + * @returns A SLIP encoded representation of our data Packet. + */ + function getSLIPPacket() { + var temp = Buffer.allocUnsafe(dataPacket.length * 2); + var i = 0; // The variable called "i" will be the current byte being written into the slip encoded output. + temp[i] = 0xc0; + i++; + for (j=0; j temp2 + temp2[i] = 0xc0; // Terminate with a slip trailder. + return temp2; + } // getSLIPPacket + + /** + * Set the data. + * @param {*} data + */ + function setData(data) { + dataPacket = data; + } + + function getData() { + return dataPacket; + } + + function toString() { + var result; + if (dataPacket[0] == 0) { + result = "Request: "; + result += "command: " + commandToString(dataPacket[1]); + result += ", size: " + dataPacket.readUInt16LE(2); + result += ", calculatedSize: " + (dataPacket.length - 8); + } else if (dataPacket[0] == 1) { + result = "Response: "; + result += "command: " + commandToString(dataPacket[1]); + result += ", size: " + dataPacket.readUInt16LE(2); + result += ", calculatedSize: " + (dataPacket.length - 8); + result += ", value: " + dataPacket.readUInt32LE(4).toString(16); + // For the ESP32 + // Size - 4 = status + // Size - 3 = error + result += ", status: " + dataPacket[dataPacket.length-4]; + result += ", errorCode: " + dataPacket[dataPacket.length-3]; + } else { + result = "Unknown command: " + dataPacket[0]; + } + return result; + } // toString + + return { + "containsSLIPPacket": containsSLIPPacket, // Does the data passed in define contain a SLIP packet? + "getSLIPPacket": getSLIPPacket, + "setSLIPPacket": setSLIPPacket, + "getData": getData, + "setData": setData, + "toString": toString + } // End of return +} // DataPacket + + +/** + * Convert a command code to a string representation. + * @param {String} command The command to be converted. + * @returns A string representation of the command. + */ +function commandToString(command) { + switch(command) { + case COMMAND_FLASH_BEGIN: + return "COMMAND_FLASH_BEGIN"; + case COMMAND_FLASH_DATA: + return "COMMAND_FLASH_DATA"; + case COMMAND_FLASH_END: + return "COMMAND_FLASH_END"; + case COMMAND_MEM_BEGIN: + return "COMMAND_MEM_BEGIN"; + case COMMAND_MEM_END: + return "COMMAND_MEM_END"; + case COMMAND_MEM_DATA: + return "COMMAND_MEM_DATA"; + case COMMAND_SYNC: + return "COMMAND_SYNC"; + case COMMAND_WRITE_REG: + return "COMMAND_WRITE_REG"; + case COMMAND_READ_REG: + return "COMMAND_READ_REG"; + case COMMAND_SPI_SET_PARAMS: + return "COMMAND_SPI_SET_PARAMS"; + case COMMAND_SPI_FLASH_MD5: + return "COMMAND_SPI_FLASH_MD5"; + } + return "Unknown command: " + command; +} // commandToString + + +function setFlags(options) { + return new Promise((resolve, reject) => { + port.set(options, () => { + resolve(); + }); + }); +} // setFlags + + +function delay(msecs) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, msecs); + }); +} // delay + + +/** + * Write the command header + * @param command // The ESP32 flash loader command + * @param data // The data to send. + * @param checksum // [Optional] The checksum value + * @returns + */ +function buildCommand(command, data, checksum = 0) { + console.log("buildCommand: %s, checksum: 0x%s, dataLength: %d", commandToString(command), checksum.toString(16), data.length); + var buf = Buffer.allocUnsafe(8 + data.length); // Create a buffer used to hold the command. + buf.writeUInt8(0, 0); // Flag as a request [0] + buf.writeUInt8(command, 1); // Write the command [1] + buf.writeUInt16LE(data.length, 2); // Write the size [2-3] + buf.writeUInt32LE(checksum, 4); // Write the checksum [4-7] + data.copy(buf, 8); // Copy data[0..] to buf[8..] + var dataPacket = DataPacket(); // Create a new data packet object + dataPacket.setData(buf); // Populate the data packet + return dataPacket; +} // writeCommand + + +/** + * Promise to flush all pending transmitting data before resolution. + * @returns A promise that is resolved when the transmission data has all been sent. + */ +async function drain() { + return new Promise((resolve, reject) => { + port.drain(() => {resolve()}); + }); +} // drain + + +/** + * Promise to discard all un-transmitted output data and discard all unread input data. + * @returns A promise that is resolved when input/output data is discarded. + */ +function flush() { + return; + console.log("Flushing..."); + return new Promise((resolve, reject) => { + port.flush(()=>{resolve()}); + }); +} // flush + +// We send a UART command request. The thinking is that we will now start to receive a UART command response. +// The response won't arrive as a unit but will instead be dribbled in over time asynchronously. As such, we need to maintain +// state to process it. A response is composed of a response header and response payload. The response header (as seen by +// UART) will contain: +// 0xC0 - SLIP header +// 0x01 - Direction. Should be 0x01 for a response. +// 0x?? - Command +// 0x?? 0x?? - Size of payload data. +// 0x?? 0x?? 0x?? 0x?? - Value. Used by some commands (NOT ALL)!! +// 0x?? ... - Data as indicated by the size command. +// 0x?? 0x?? 0x?? 0x?? - Outcome +// 0xC0 - SLIP trailer +function ProcessResponse() { + var command; + var data; + var dataSize; + var value; + var status; + var errorCode; + var resolve; + var reject = null; + var promise = null; + + var timeoutId; + + + async function getResponse() { + console.log(">> ProcessResponse.getResponse()") + + promise = new Promise(async (resolveA, rejectA) => { + resolve = resolveA; + reject = rejectA; + timeoutId = setTimeout(()=>{ + console.log("getResponse timeout!"); + reject(); + }, 500); + var dp = await serialData.getNextDataPacket(); + console.log(" .. getResponse ... we think we have a new datapacket!"); + processPacket(dp.getData()); + resolve(); + }); + return promise; + } + + function processPacket(inputData) { + assert(inputData); + assert(inputData.length > 0); + console.log("Received a packet, length %d:\n", inputData.length, hexy.hexy(inputData)); + // 0 - direction + // 1 - command + // [2-3] - size + // [4-7] - value + // [8-(length-3)] - data + // [length-2] - status + // [length-1] - error code + if (inputData.readUInt8(0) != 0x01) { + throw "Expected direction to be 0x01 for a response."; + } + command = inputData.readUInt8(1); + dataSize = inputData.readUInt16LE(2); + value = inputData.readUInt32LE(4); + data = Buffer.allocUnsafe(inputData.length - 8); + inputData.copy(data, 0, 8, inputData.length-2); + status = inputData.readUInt8(inputData.length - 2); + erorCode = inputData.readUInt8(inputData.length-1); + clearTimeout(timeoutId); + } // processPacket + + return { + getResponse: getResponse, + processPacket: processPacket + }; +} // ProcessResponse + + +/** + * Send a SYNC command and wait for the response. + */ +async function doSync() { + console.log("Sending sync"); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(36); // The sync message is 0x07 0x07 0x12 0x20 0x55 (repeated 32 times) + buf.writeUInt8(0x07, 0); + buf.writeUInt8(0x07, 1); + buf.writeUInt8(0x12, 2); + buf.writeUInt8(0x20, 3); + for (var i=4; i<36; i++) { + buf.writeUInt8(0x55, i); + } + var dataPacket = buildCommand(COMMAND_SYNC, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + console.log("response: " + response); + try { + await response.getResponse(); + resolve(); + } + catch(e) { + console.log("Failed to get SYNC command response: " + e); + reject(); + } + }); + return p; +} // doSync + + +/** + * Calculate the checksum of the data. + * The current algorithm is the seed 0xEF XORed with each of the bytes of data. + * @param {Buffer} data The data against which the checksum is to be calculated. + * @returns The checksum value. + */ +function calculateChecksum(data) { + var checksum = 0xef; + for (var i=0; i> memEnd: executeFlag: %d, entrypointAddress: 0x%s", executeFlag, entrypointAddress.toString(16)); + var p = new Promise(async function (resolve, reject) { + await flush(); // Discard any pending input or output. + var buf = Buffer.allocUnsafe(2*4); // Allocate the payload which is 2 * 32bit words + buf.writeUInt32LE(executeFlag, 0); // Execute flag. + buf.writeUInt32LE(entrypointAddress, 4); // Entry point address. + var dataPacket = buildCommand(COMMAND_MEM_END, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); // Drain the transmit queue. + await response.getResponse(); // Wait for a response. + resolve(); // Resolve the promise. + }); + return p; + } // memEnd + + /** + * Determine if we have sent all the data for the given MEM_BEGIN transaction. + * @returns True if we have sent all the data we need to send. + */ + function allDataSent() { + // We maintain state in a number of variables. The variable called sizeWritten is the number of bytes that we have written to + // the ESP32. The variable called totalSize is the number of bytes that we expect to write. The notion of all data sent + // will occur when the sizeWritten is equal to totalSize. + return sizeWritten >= totalSize; + } // allDataSent + + + /** + * Write a MEM_DATA command. + * This command should follow a MEM_BEGIN command. If transmits a unit of data that is written directly into + * the ESP32 RAM. The address in RAM is supplied by the MEM_BEGIN command and previous MEM_DATA calls. + * @param {Buffer} data + */ + async function memData(data) { + console.log(">> memData: sequenceId: %d", sequenceNumber); + var sizeRemaining = totalSize - sizeWritten; // Calculate the amount of data still expected. + var offsetIntoData = sequenceNumber * MEM_PACKET_SIZE; // Calculate the offset into the data from which to send. + var sizeToWrite = sizeRemaining > MEM_PACKET_SIZE?MEM_PACKET_SIZE:sizeRemaining; // Calculate how large this unit should be. + var buf2 = Buffer.allocUnsafe(4*4 + data.length); // Allocate storage for the command preamble. + buf2.writeUInt32LE(sizeToWrite, 0); // Size of data to be sent. + buf2.writeUInt32LE(sequenceNumber, 4); // Current sequence number. + buf2.writeUInt32LE(0, 8); // Zero. + buf2.writeUInt32LE(0, 12); // Zero. + data.copy(buf2, 16); + //var buf = Buffer.concat([buf2, data]); + //console.log("Here is the data we are sending:\n%s", hexy.hexy(buf)); + + var p = new Promise(async function(resolve, reject) { + console.log("+----------+"); + console.log("| MEM_DATA |"); + console.log("+----------+"); + await flush(); // Discard any untransmitted and unreceived data. // End the SLIP communication. + var dataPacket = buildCommand(COMMAND_MEM_DATA, buf2, calculateChecksum(data)); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); // Drain the transmit queue. + sequenceNumber ++; // Increment the sequence number. + sizeWritten += sizeToWrite; // Updae the size of the data that we have now written. // Ensure that all the transmitted data is now outbound. + await response.getResponse(); // Await a response. + resolve(); // Indicate that the command has been completed. + }); + return p; + }; // memData + + + /** + * Transmit a buffer of data to a given address within ESP32 RAM. + * @param {Buffer} data The data to be written into ESP32 RAM. + * @param {integer} address The address that data will be written to. + */ + async function send(data, address) { + // If the data is not 32bit aligned, expand to 32 bits. + if (data.length % 4 != 0) { + let buf = Buffer.allocUnsafe((data.length + 4) & 0xFFFFFFFC).fill(0xff); + data.copy(buf); + data = buf; + } + console.log(">> send data of size: %d, to RAM address 0x%s", data.length, address.toString(16)); + await memBegin(data.length, address); + while(!allDataSent()) { + await memData(data); + } + }; // send + + + return { + "send": send, + "end": memEnd + }; +} // MemCommands + +function portWrite(data) { + console.log("Writing data down port, length: %d", data.length) + return new Promise((resolve, reject) => { + port.write(data, ()=> {resolve()}); + }); +} + +/** + * Process the handling of flash commands. + */ +function FlashCommands() { + var sequenceNumber; // The current sequence number. + var totalSize; // The total number of bytes we expect to send. + var sizeWritten; // The number of bytes we have written so far. + + const FLASH_PACKET_SIZE = 0x4000; // 16K = 16384 bytes + + + async function flashBegin(size, address) { + console.log("Flash Begin: size: %d, address: 0x%s", size, address.toString(16)); + var p = new Promise(async function (resolve, reject) { + console.log("+-------------+"); + console.log("| FLASH_BEGIN |"); + console.log("+-------------+"); + await flush(); + var buf = Buffer.allocUnsafe(4*4); + buf.writeUInt32LE(size, 0); // Size of data to be sent/erased. + buf.writeUInt32LE(Math.ceil(size/FLASH_PACKET_SIZE), 4); // Number of data packets to be sent. + buf.writeUInt32LE(FLASH_PACKET_SIZE, 8); // Size of each data packet. + buf.writeUInt32LE(address, 12); // Offset in memory to start writing. + var dataPacket = buildCommand(COMMAND_FLASH_BEGIN, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + await response.getResponse(); + resolve(); + }); + return p; + } // flashBegin + + + async function flashData(data, sequenceNumber) { + console.log(">> flashData: sequenceId: %d, size: %d", sequenceNumber, data.length); + if (data.length < 16*1024) { + var temp = Buffer.alloc(16*1024, 0xff); + data.copy(temp); + data = temp; + } + var buf2 = Buffer.alloc(4*4 + data.length); // Allocate storage for the command preamble. + buf2.writeUInt32LE(data.length, 0); // Size of data to be sent. + buf2.writeUInt32LE(sequenceNumber, 4); // Current sequence number. + buf2.writeUInt32LE(0, 8); + buf2.writeUInt32LE(0, 12); + data.copy(buf2, 16); + //var buf = Buffer.concat([buf2, data]); + //console.log("Here is the data we are sending:\n%s", hexy.hexy(buf)); + + var p = new Promise(async function(resolve, reject) { + console.log("+------------+"); + console.log("| FLASH_DATA |"); + console.log("+------------+"); + await flush(); // Discard any untransmitted and unreceived data. + var dataPacket = buildCommand(COMMAND_FLASH_DATA, buf2, calculateChecksum(data)); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); // Ensure that all the transmitted data is now outbound. + + await response.getResponse(); // Await a response. + resolve(); // Indicate that the command has been completed. + }); + return p; + }; // flashData + + + /** + * Send the FLASH_END command. + * @param {Integer} command + * @returns A promise that is resolved when the FLASH_END command completes. + */ + function flashEnd(command) { + console.log("+-----------+"); + console.log("| FLASH_END |"); + console.log("+-----------+"); + console.log(">> memEnd: command: %d", command); + var p = new Promise(async function (resolve, reject) { + await flush(); // Discard any pending input or output. + var buf = Buffer.allocUnsafe(4); // Allocate the payload which is one 32bit word. + buf.writeUInt32LE(command, 0); // Execute flag. + var dataPacket = buildCommand(COMMAND_FLASH_END, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); // Drain the transmit queue. + await response.getResponse(); // Wait for a response. + resolve(); // Resolve the promise. + }); + return p; + } // flashEnd + + /** + * Transmit a buffer of data to a given address within ESP32 FLASH. + * @param {Buffer} data The data to be written into ESP32 FLASH. + * @param {integer} address The address that data will be written to. + */ + async function send(data, address) { + return new Promise(async (resolve, reject) => { + // If the data is not 32bit aligned, expand to 32 bits. + if (data.length % 4 != 0) { + let buf = Buffer.allocUnsafe((data.length + 4) & 0xFFFFFFFC).fill(0xff); + data.copy(buf); + data = buf; + } + console.log(">> send flash data of size: %d, to flash address 0x%s", data.length, address.toString(16)); + await flashBegin(data.length, address); + + sequenceNumber = 0; // The sequence number of the next FLASH_DATA transmission. + totalSize = data.length; // The size of the data we are going to be sending. + sizeWritten = 0; // The size of the data we have written so far. + + while(sizeWritten < totalSize) { + var sizeRemaining = totalSize - sizeWritten; // Calculate the amount of data still expected. + var offsetIntoData = sequenceNumber * FLASH_PACKET_SIZE; // Calculate the offset into the data from which to send. + var sizeToWrite = sizeRemaining > FLASH_PACKET_SIZE?FLASH_PACKET_SIZE:sizeRemaining; // Calculate how large this unit should be. + var tempBuf = Buffer.allocUnsafe(sizeToWrite); + data.copy(tempBuf, 0, offsetIntoData, offsetIntoData+sizeToWrite); + await flashData(tempBuf, sequenceNumber); + sequenceNumber++; + sizeWritten += sizeToWrite; + } + await flashEnd(1); + resolve(); + }); + }; // send + + return { + "send": send, + "end": flashEnd + }; + +} // FlashCommands + + +/** + * Upload the flasher application. + * @returns A Promise that is fulfilled when the flasher has been uploaded. + */ +async function uploadFlasher() { + // We read a JSON file that has been prepared to contain the flasher application. This JSON contains: + // { + // textAddress: - A string representation (hex) of the memory address into which the text data should be loaded. + // dataAddress: - A string representation (hex) of the memory address into which the data data should be loaded. + // textData: - A base64 encoded representation of the text data. + // dataData: - A base64 encoded representation of the data data + // entryPoint: - A string representation of the entry point (hex) for execution. + // } + // + return new Promise(async (resolve, reject)=>{ + console.log(">> uploadFlasher"); + // Load the flasher from the local file. + var flasherDataRaw = fs.readFileSync("esptool/flasher_stub/build/flasher_stub.json", "utf8"); + var flasherData = JSON.parse(flasherDataRaw); + + flasherData.textData = Buffer.from(flasherData.textData, "base64"); + flasherData.dataData = Buffer.from(flasherData.dataData, "base64"); + console.log("Text data length: %d, Data data length: %d", flasherData.textData.length, flasherData.dataData.length); + + await MemCommands().send(flasherData.textData, parseInt(flasherData.textAddress, 16)); + await MemCommands().send(flasherData.dataData, parseInt(flasherData.dataAddress, 16)); + await MemCommands().end(0, parseInt(flasherData.entryPoint, 16)); + console.log("<< uploadFlasher complete"); + resolve(); + }); // End of promise +} // uploadFlasher + + +/** + * Write a value to an ESP32 register defined. + * @param {Integer} address The address of the register. + * @param {Integer} value The value to be written to the register. + * @returns A promise that is fulfilled when the register has been written. + */ +function writeReg(address, value) { + console.log("+-----------+"); + console.log("| WRITE_REG |"); + console.log("+-----------+"); + console.log(">> Writing register: Command: 0x%s, Address: 0x%s, value: 0x%s", + COMMAND_WRITE_REG.toString(16), address.toString(16), value.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4*4); + buf.writeUInt32LE(address, 0); + buf.writeUInt32LE(value, 4); + buf.writeUInt32LE(0xffffffff, 8); // Mask + buf.writeUInt32LE(0, 12); // Delay + var dataPacket = buildCommand(COMMAND_WRITE_REG, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + await response.getResponse(); + resolve(); + }); + return p; +} // writeReg + +/** + * Set the SPI configuration parameters. + * @param {Integer} size The size of SPI flash. + * @returns A promise that is fulfilled when the SPI parameters have been set. + */ +function spiSetParams(size) { + console.log("+----------------+"); + console.log("| SPI_SET_PARAMS |"); + console.log("+----------------+"); + console.log(">> SPI Set Params: Command: 0x%s, size:%d", + COMMAND_SPI_SET_PARAMS.toString(16), size); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(6*4); + buf.writeUInt32LE(0, 0); // id + buf.writeUInt32LE(size, 4); // Total size + buf.writeUInt32LE(64*1024, 8); // block size + buf.writeUInt32LE(4*1024, 12); // sector size + buf.writeUInt32LE(256, 16); // page size + buf.writeUInt32LE(0xffff, 20); // status mask + var dataPacket = buildCommand(COMMAND_SPI_SET_PARAMS, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + await response.getResponse(); + resolve(); + }); + return p; +} // writeReg + + +/** + * Read an ESP32 register. + * @param {Integer} address The address of the register to be read. + * @returns The result of reading the register. + */ +function readReg(address) { + console.log(">> Reading register: Command: 0x%s, Address: 0x%s", COMMAND_READ_REG.toString(16), address.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4); + buf.writeUInt32LE(address, 0); + var dataPacket = buildCommand(COMMAND_READ_REG, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + var result = await response.getResponse(); + console.log("readReg: %O", result); + resolve(result); + }); + return p; +} // readReg + + + +/** + * Get the MD5 hash of an area of flash memory. + * @param {Integer} address The address of the flash memory. + * @param {Integer} size The size of the flasg memory. + * @returns The MD5 hash value. + */ +function spiFlashMD5(address, size) { + console.log(">> spiFlashMD5: Command: 0x%s, Address: 0x%s, Size: %d", COMMAND_SPI_FLASH_MD5.toString(16), address.toString(16), size); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4 * 4); + buf.writeUInt32LE(address, 0); + buf.writeUInt32LE(size, 0); + buf.writeUInt32LE(0, 0); + buf.writeUInt32LE(0, 0); + var dataPacket = buildCommand(COMMAND_SPI_FLASH_MD5, buf); + await portWrite(dataPacket.getSLIPPacket()); + await drain(); + var result = await response.getResponse(); + console.log("spiFlashMD5 result: %O", result); + resolve(result); + }); + return p; +} // spiFlashMD5 + + +/** + * Read the value of an eFuse. + * @param {Integer} fuseNumber + * @returns The value of the eFuse. + */ +function readEfuse(fuseNumber) { + console.log(">> readEFuse: %d", fuseNumber); + return readReg(EFUSE_REG_BASE + (4 * fuseNumber)); +} // readEfuse + + +/** + * The ESP32 needs to be in a "download" mode in order to process commands. For many boards, a sequence of + * DTR/RTS UART twiddles can be sends to achieve that goal. This function performs that task. + * + * @returns A Promise that is fulfilled when the ESP32 is in download mode. + */ +function enterDownloadMode() { + console.log(">> enterDownloadMode: Enter download mode, esp32r0Delay = " + esp32r0Delay); + return new Promise((resolve, reject) => { + setFlags({ dtr: false, rts: true }).then(()=>{ + return delay(100); + }).then(() => { + if (esp32r0Delay) { + return delay(1200); + } + return Promise.resolve(); + }).then(()=> { + return setFlags({ dtr: true, rts: false }); + }).then(() => { + if (esp32r0Delay) { + return delay(400); + } + return Promise.resolve(); + }).then(()=> { + return delay(50); + }).then(()=> { + return setFlags({ dtr: false, rts: false }); + }).then(() => { + resolve(); + }); + }); +} // enterDownloadMode + + +/** + * Flash the content of the file supplied by filename into ESP32 flash specified by the address. + * @param {Integer} address The address in flash to write the file. + * @param {String} fileName The name of the file to read from. + */ +async function flashFile(address, fileName) { + console.log("Flashing file %s to address 0x%s", fileName, address.toString(16)); + var fileData = fs.readFileSync(fileName); + console.log("Read file ... size is %d", fileData.length); + await FlashCommands().send(fileData, address); + console.log("Flash File complete"); +} // flashFile + + +async function sleep(interval) { + return new Promise((resolve) => { + setTimeout(() => {resolve()}, interval); + }); +} + + +console.log("Start!"); +if (argv._.length%2 != 0) { + console.log("Wrong number of args"); + return; +} +response = ProcessResponse(); +console.log("Response created: " + response); +port.open(async (err)=>{ + if (err) { + console.log("Error opening: " + err); + return; + } + console.log("Open!"); + await enterDownloadMode(); + //var p = Promise.resolve(); + console.log("We should now be in ESP32 download mode!"); + serialData.empty(); + while(1) { + try { + await doSync(); + break; + } + catch(e) { + console.log(e); + } + } + await sleep(1000); + serialData.empty(); + console.log("+----------------+") + console.log("| Sync Complete! |"); + console.log("+----------------+") + + await readEfuse(3); + console.log("Read fuse completed!"); + await uploadFlasher(); + serialData.empty(); + console.log("Flasher upload completed and running!"); + /* + await writeReg(0x6000202c, 0x00000017); + await writeReg(0x6000201c, 0x90000000); + await writeReg(0x60002024, 0x9f000070); + await writeReg(0x60002080, 0x00000000); + await writeReg(0x60002000, 0x00040000); + await writeReg(0x6000201c, 0x80000040); + await writeReg(0x60002024, 0x00000000); + */ + await spiSetParams(0x400000); + + // Walk through each of the files presented and upload them. The input on the command line should be of the form: + // address file [address file]* + + for (var i=0; i { + serialData.append(data); // Append the new data into the serial buffer. +}); + +console.log("Init done"); From a60e6dead22393ec9ea717782dd32775689ab1e8 Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Thu, 12 Apr 2018 12:10:53 -0700 Subject: [PATCH 195/310] Added (long)response_code return value to get and post methods so that you can test for success --- cpp_utils/RESTClient.cpp | 10 ++++++++-- cpp_utils/RESTClient.h | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cpp_utils/RESTClient.cpp b/cpp_utils/RESTClient.cpp index 20b5bc3d..7160dca6 100644 --- a/cpp_utils/RESTClient.cpp +++ b/cpp_utils/RESTClient.cpp @@ -36,13 +36,16 @@ RESTClient::~RESTClient() { /** * @brief Perform an HTTP GET request. */ -void RESTClient::get() { +long RESTClient::get() { + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_HTTPGET, 1); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { ESP_LOGE(tag, "get(): %s", getErrorMessage().c_str()); } + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code); // Added return response_code 2018_4_12 + return response_code; // Added return response_code 2018_4_12 } // get @@ -52,13 +55,16 @@ void RESTClient::get() { * @param [in] body The body of the payload to send with the post request. * */ -void RESTClient::post(std::string body) { +long RESTClient::post(std::string body) { + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_POSTFIELDS, body.c_str()); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { ESP_LOGE(tag, "post(): %s", getErrorMessage().c_str()); } + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code);// Added return response_code 2018_4_12 + return response_code;// Added return response_code 2018_4_12 } // post diff --git a/cpp_utils/RESTClient.h b/cpp_utils/RESTClient.h index 02279c40..4ad91d5a 100644 --- a/cpp_utils/RESTClient.h +++ b/cpp_utils/RESTClient.h @@ -68,7 +68,7 @@ class RESTClient { RESTClient(); virtual ~RESTClient(); void addHeader(std::string name, std::string value); - void get(); + long get(); // Added return response_code 2018_4_12 std::string getErrorMessage(); /** * @brief Get the response payload data from the last REST call. @@ -86,7 +86,7 @@ class RESTClient { return m_timings; } - void post(std::string body); + long post(std::string body); // Added return response_code 2018_4_12 /** * @brief Set the URL for the target. From b87e0ad857fa6006430ed09e4aa990853c4987dd Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Thu, 12 Apr 2018 20:48:17 -0700 Subject: [PATCH 196/310] Modified bootWiFi2 method to call m_wifi.connectAP forever or until it successfully connects --- networking/bootwifi/BootWiFi.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index aadc6d34..8a62786a 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -285,7 +285,11 @@ void BootWiFi::bootWiFi2() { connectionInfo.ipInfo.gw.addr, connectionInfo.ipInfo.netmask.addr ); - m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password); // Connect to the access point. + + // Connect to the access point. + while(!m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password)){ + ESP_LOGE(LOG_TAG, "Unable to connect to access point \"%s\" - trying again...", connectionInfo.ssid); + }; } else { // We do NOT have connection information. Let us now become an access From 472d1517519065d7b218f5aaa49b59344195abb2 Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 13 Apr 2018 21:29:39 +0200 Subject: [PATCH 197/310] Prevents exception when parsing HTTP POST message without a body --- cpp_utils/HttpParser.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index cc7b686b..d9040659 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -220,7 +220,9 @@ void HttpParser::parse(Socket s) { } else { uint8_t data[512]; int rc = s.receive(data, sizeof(data)); - m_body = std::string((char *)data, rc); + if (rc > 0) { + m_body = std::string((char *)data, rc); + } } ESP_LOGD(LOG_TAG, "<< parse: Size of body: %d", m_body.length()); } // parse From 7a33f4bf8ade6f6da31a2ae86d2a98fd28c9d290 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 17 Apr 2018 22:54:09 -0500 Subject: [PATCH 198/310] Refresh of ESP32 flash tool libraries --- tools/esptool_libs/nodejs/README.md | 3 + tools/esptool_libs/nodejs/build.sh | 25 +++ tools/esptool_libs/nodejs/espjs.js | 284 +++++++++++++++++----------- 3 files changed, 198 insertions(+), 114 deletions(-) create mode 100644 tools/esptool_libs/nodejs/README.md create mode 100755 tools/esptool_libs/nodejs/build.sh diff --git a/tools/esptool_libs/nodejs/README.md b/tools/esptool_libs/nodejs/README.md new file mode 100644 index 00000000..95d560e6 --- /dev/null +++ b/tools/esptool_libs/nodejs/README.md @@ -0,0 +1,3 @@ +# NodeJS ESP Tools + +The `build.js` tool is used to take the `stub_flasher_32.elf` and break it apart into the separate files that need to be individually pushed to ESP32 RAM for execution. \ No newline at end of file diff --git a/tools/esptool_libs/nodejs/build.sh b/tools/esptool_libs/nodejs/build.sh new file mode 100755 index 00000000..8bcf71eb --- /dev/null +++ b/tools/esptool_libs/nodejs/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Extract the text and data sections to their own files. +xtensa-esp32-elf-objcopy \ + --dump-section .text=text.dat \ + --dump-section .data=data.dat \ + stub_flasher_32.elf + +# Parse out the text load address, the data load address and the entry point from the ELF. +text_address=`xtensa-esp32-elf-objdump --section-headers --wide --section=.text stub_flasher_32.elf | sed -n '$s/^\s*[[:alnum:]]*\s*\S*\s*[[:alnum:]]*\s*[[:alnum:]]*\s*\([[:alnum:]]*\).*/\1/p'` +#echo "Text address: 0x${text_address}" +data_address=`xtensa-esp32-elf-objdump --section-headers --wide --section=.data stub_flasher_32.elf | sed -n '$s/^\s*[[:alnum:]]*\s*\S*\s*[[:alnum:]]*\s*[[:alnum:]]*\s*\([[:alnum:]]*\).*/\1/p'` +#echo "Data address: 0x${data_address}" +entry_point=`xtensa-esp32-elf-objdump --wide --file-headers stub_flasher_32.elf | sed -n '/start address/s/start address 0x\([[:alnum:]]\)/\1/p'` +#echo "Entry point: 0x${entry_point}" + +echo "{" +echo " \"textAddress\": \"${text_address}\"," +echo " \"dataAddress\": \"${data_address}\"," +echo " \"entryPoint\": \"${entry_point}\"," +echo " \"textData\": \"$(base64 --wrap=0 text.dat)\"," +echo " \"dataData\": \"$(base64 --wrap=0 data.dat)\"" +echo "}" + +# Remove the temporary text and data files. +#rm text.dat data.dat diff --git a/tools/esptool_libs/nodejs/espjs.js b/tools/esptool_libs/nodejs/espjs.js index 5285e265..33b62e68 100644 --- a/tools/esptool_libs/nodejs/espjs.js +++ b/tools/esptool_libs/nodejs/espjs.js @@ -12,6 +12,23 @@ * */ + /** + * High level commands: + * doSync - SYNC + * MemCommands + * - MEM_BEGIN + * - MEM_END + * FlashCommands + * - FLASH_BEGIN + * - FLASH_END + * writeReg - WRITE_REG + * readReg - READ_REG + * enterDownloadMode + * spiSetParams - SPI_SET_PARAMS + * flashFile - Flash a specific file to a specific address. + * spiFlashMD5 - Calculate the hash of a region of flash. + */ + /** * We want to provide processing to both transmitted requests and received responses. A request is a message that is then encapsulated * in a SLIP packet and transmitted via UART. A response is a message received via UART that is wrapped in a SLIP packet. @@ -19,32 +36,45 @@ * We will logically define a DataPacket as the raw packet unit that is either transmitted or received. This encapsulates a * Buffer that contains the data. */ -const hexy = require("hexy"); -const zlib = require('zlib'); -const fs = require("fs"); -const assert = require("assert"); -const PORT = "/dev/ttyUSB1"; -const argv = require("minimist")(process.argv.slice(2)); - -const COMMAND_FLASH_BEGIN = 0x02; -const COMMAND_FLASH_DATA = 0x03; -const COMMAND_FLASH_END = 0x04; -const COMMAND_MEM_BEGIN = 0x05; -const COMMAND_MEM_END = 0x06; -const COMMAND_MEM_DATA = 0x07; -const COMMAND_SYNC = 0x08; -const COMMAND_WRITE_REG = 0x09; -const COMMAND_READ_REG = 0x0a; -const COMMAND_SPI_SET_PARAMS = 0x0b; -const COMMAND_SPI_FLASH_MD5 = 0x13; - -const EFUSE_REG_BASE = 0x6001a000; +const hexy = require("hexy"); // Dumping hex data utility. +const zlib = require('zlib'); // Compression/decompression. +const fs = require("fs"); // File system access. +const assert = require("assert"); // Internal assertion. +const argv = require("minimist")(process.argv.slice(2)); // command line processing. +const SerialPort = require("serialport"); // Serial / UART access. +const md5 = require("md5"); // Hash checksum. + +const PORT = "/dev/ttyUSB1"; // The serial port to work with. + +// These are the commands exposed by the Flasher stub. +const COMMAND_FLASH_BEGIN = 0x02; +const COMMAND_FLASH_DATA = 0x03; +const COMMAND_FLASH_END = 0x04; +const COMMAND_MEM_BEGIN = 0x05; +const COMMAND_MEM_END = 0x06; +const COMMAND_MEM_DATA = 0x07; +const COMMAND_SYNC = 0x08; +const COMMAND_WRITE_REG = 0x09; +const COMMAND_READ_REG = 0x0a; +const COMMAND_SPI_SET_PARAMS = 0x0b; +const COMMAND_SPI_ATTACH = 0x0d; +const COMMAND_CHANGE_BAUDRATE = 0x0f; +const COMMAND_FLASH_DEFL_BEGIN = 0x10; +const COMMAND_FLASH_DEFL_DATA = 0x11; +const COMMAND_FLASH_DEFL_END = 0x12; +const COMMAND_SPI_FLASH_MD5 = 0x13; +const COMMAND_ERASE_FLASH = 0xd0; +const COMMAND_ERASE_REGION = 0xd1; +const COMMAND_READ_FLASH = 0xd2; +const COMMAND_RUN_USER_CODE = 0xd3; + +const EFUSE_REG_BASE = 0x6001a000; var esp32r0Delay = false; var response = null; var serialData = SerialData(); // The buffer holding the received serial data. -var SerialPort = require("serialport"); + var port = new SerialPort(PORT, { baudRate: 115200, autoOpen: false @@ -52,18 +82,24 @@ var port = new SerialPort(PORT, { /** * Maintain received serial data. + * * We are receiving serial data asynchronosly from the serial processor. This means that when data arrives * on the UART, it is passed to an instance of this object for accumulation. The data is passed in through a - * call to append. + * call to append(). */ function SerialData() { - var serialDataBuffer; // The serial data we are accumulating. + var serialDataBuffer; // The serial data we are accumulating. + var resolveMoreData = null; // A resolve function for a promise that will be resolved when more data has arrived. + + /** + * Purge and reset any existing unprocessed serial data. + */ function empty() { serialDataBuffer = Buffer.allocUnsafe(0); - } + } // empty + - var resolveMoreData = null; /** * Append data received from the serial port into our buffer. * @param {Buffer} data The data received from our serial port. @@ -71,13 +107,14 @@ function SerialData() { */ function append(data) { console.log("SerialData.append:\n%s", hexy.hexy(data)); - serialDataBuffer = Buffer.concat([serialDataBuffer, data]); // Append the new data into the serial buffer. + serialDataBuffer = Buffer.concat([serialDataBuffer, data]); // Append the new data into the accumulating serial buffer. if (resolveMoreData != null) { // If we have a promise resolution that is waiting for more data .. resolveMoreData(); // resolve the promise. resolveModeData = null; } } // append + /** * Create a promise that is resolved when we have more data. * @returns A promise that is resolved when we have more data. @@ -90,18 +127,20 @@ function SerialData() { /** + * Get the next SLIP data packet. * Assume that the serial data received is a sequence of packets. This function returns a promise that * is resolved when a packet is available for processing. */ async function getNextDataPacket() { var p = new Promise(async (resolve, reject) => { console.log("getNextDataPacket>>"); - while(true) { // Keep looping until we have a data packet. - if (DataPacket().containsSLIPPacket(serialDataBuffer)) { // Does the buffer now contain a SLIP packet? - var dp = DataPacket(); // Create a new data packet - serialDataBuffer = dp.setSLIPPacket(serialDataBuffer); // Load the data packet with the raw serial data. - console.log("Got a new data packet: %s\n%s", dp.toString(), hexy.hexy(dp.getData())); - resolve(dp); + while(true) { // Keep looping until we have a data packet. + if (DataPacket().containsSLIPPacket(serialDataBuffer)) { // Does the buffer now contain a SLIP packet? + var newDataPacket = DataPacket(); // Create a new data packet + serialDataBuffer = newDataPacket.setSLIPPacket(serialDataBuffer); // Load the data packet with the raw serial data. + console.log("Got a new data packet: %s\n%s", + newDataPacket.toString(), hexy.hexy(newDataPacket.getData())); + resolve(newDataPacket); return; } await moreData(); // We didn't find a data packet in our data, so wait for more data. @@ -110,10 +149,11 @@ function SerialData() { return p; // Return the promise that is resolved when we have a data packet. } // getNextDataPacket + empty(); // Initialize / empty the serial data. return { - "append": append, - "empty": empty, + "append": append, + "empty": empty, "getNextDataPacket": getNextDataPacket } } // SerialData @@ -124,6 +164,7 @@ function SerialData() { */ function DataPacket() { var dataPacket = Buffer.allocUnsafe(0); + /** * Examine the data buffer looking for a slip trailer (0xc0). If found, we return the index * at which it was found. If not found, we return -1. @@ -157,15 +198,17 @@ function DataPacket() { */ function setSLIPPacket(data) { console.log("setSLIPPacket:\n%s", hexy.hexy(data)); - assert(data); - assert(data[0] == 0xc0); - assert(data.length > 0); - var end = getSLIPTrailer(data); + assert(data); // Assert if we have been presented no data. + assert(data.length > 0); // Assert that the data length is not 0. + assert(data[0] == 0xc0); // Assert if the first bytes isn't a SLIP marker. + + var end = getSLIPTrailer(data); // Get the end marker for the SLIP packet. if (end == -1) { throw "No SLIP trailer in data we are to process."; } assert(end > 0); - dataPacket = Buffer.allocUnsafe(data.length); // Our data will be no MORE than data.length bytes in size. + + dataPacket = Buffer.allocUnsafe(data.length); // Our data will be no MORE (but likely less) than data.length bytes in size. for (var i=1, j=0; i 0xc0 dataPacket[j] = 0xc0; @@ -181,7 +224,7 @@ function DataPacket() { // We now have a buffer (dataPacket) that contains our decoded data but will be too large. However, j contains the number of bytes we want. var temp = Buffer.allocUnsafe(j); dataPacket.copy(temp, 0, 0, j); // Copy dataPacket[0..j-1] -> temp - dataPacket = temp; + dataPacket = temp; // Set datapacket to the shrunk buffer. // We have now processed what we wanted from the incoming data and now we must return the unused data. i++; @@ -190,7 +233,9 @@ function DataPacket() { data.copy(temp, 0, i); // copy data[i..] -> temp console.log("setSLIPPacket: New data has length: %d, returning unused buffer of size: %d", dataPacket.length, temp.length); - console.log("Unused:\n%s", hexy.hexy(temp)); + if (temp.length > 0) { + console.log("Unused:\n%s", hexy.hexy(temp)); + } return temp; } // setSLIPPacket @@ -251,8 +296,8 @@ function DataPacket() { // For the ESP32 // Size - 4 = status // Size - 3 = error - result += ", status: " + dataPacket[dataPacket.length-4]; - result += ", errorCode: " + dataPacket[dataPacket.length-3]; + result += ", status: " + dataPacket[dataPacket.length-2]; + result += ", errorCode: " + dataPacket[dataPacket.length-1]; } else { result = "Unknown command: " + dataPacket[0]; } @@ -329,18 +374,18 @@ function delay(msecs) { * @param checksum // [Optional] The checksum value * @returns */ -function buildCommand(command, data, checksum = 0) { - console.log("buildCommand: %s, checksum: 0x%s, dataLength: %d", commandToString(command), checksum.toString(16), data.length); +async function buildAndSendRequest(command, data, checksum = 0) { + console.log("buildAndSendRequest: %s, checksum: 0x%s, dataLength: %d", commandToString(command), checksum.toString(16), data.length); var buf = Buffer.allocUnsafe(8 + data.length); // Create a buffer used to hold the command. - buf.writeUInt8(0, 0); // Flag as a request [0] - buf.writeUInt8(command, 1); // Write the command [1] + buf.writeUInt8(0, 0); // Flag as a request [0] + buf.writeUInt8(command, 1); // Write the command [1] buf.writeUInt16LE(data.length, 2); // Write the size [2-3] - buf.writeUInt32LE(checksum, 4); // Write the checksum [4-7] + buf.writeUInt32LE(checksum, 4); // Write the checksum [4-7] data.copy(buf, 8); // Copy data[0..] to buf[8..] var dataPacket = DataPacket(); // Create a new data packet object dataPacket.setData(buf); // Populate the data packet - return dataPacket; -} // writeCommand + await portWrite(dataPacket.getSLIPPacket()); // Send the data packet to the ESP32. +} // buildAndSendRequest /** @@ -401,7 +446,7 @@ function ProcessResponse() { timeoutId = setTimeout(()=>{ console.log("getResponse timeout!"); reject(); - }, 500); + }, 1000); var dp = await serialData.getNextDataPacket(); console.log(" .. getResponse ... we think we have a new datapacket!"); processPacket(dp.getData()); @@ -413,7 +458,7 @@ function ProcessResponse() { function processPacket(inputData) { assert(inputData); assert(inputData.length > 0); - console.log("Received a packet, length %d:\n", inputData.length, hexy.hexy(inputData)); + console.log("Received a packet, length %d:\n%s", inputData.length, hexy.hexy(inputData)); // 0 - direction // 1 - command // [2-3] - size @@ -427,16 +472,18 @@ function ProcessResponse() { command = inputData.readUInt8(1); dataSize = inputData.readUInt16LE(2); value = inputData.readUInt32LE(4); - data = Buffer.allocUnsafe(inputData.length - 8); - inputData.copy(data, 0, 8, inputData.length-2); + data = Buffer.allocUnsafe(inputData.length - 8 - 2); + inputData.copy(data, 0, 8, inputData.length - 2); status = inputData.readUInt8(inputData.length - 2); - erorCode = inputData.readUInt8(inputData.length-1); + erorCode = inputData.readUInt8(inputData.length - 1); clearTimeout(timeoutId); } // processPacket return { getResponse: getResponse, - processPacket: processPacket + getData: function() { + return data; + } }; } // ProcessResponse @@ -456,9 +503,7 @@ async function doSync() { for (var i=4; i<36; i++) { buf.writeUInt8(0x55, i); } - var dataPacket = buildCommand(COMMAND_SYNC, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + await buildAndSendRequest(COMMAND_SYNC, buf); console.log("response: " + response); try { await response.getResponse(); @@ -508,9 +553,7 @@ function MemCommands() { buf.writeUInt32LE(Math.ceil(size/MEM_PACKET_SIZE), 4); // Number of data packets to be sent. buf.writeUInt32LE(MEM_PACKET_SIZE, 8); // Size of each data packet. buf.writeUInt32LE(address, 12); // Offset in memory to start writing. - var dataPacket = buildCommand(COMMAND_MEM_BEGIN, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + await buildAndSendRequest(COMMAND_MEM_BEGIN, buf); await response.getResponse(); resolve(); }); @@ -531,9 +574,7 @@ function MemCommands() { var buf = Buffer.allocUnsafe(2*4); // Allocate the payload which is 2 * 32bit words buf.writeUInt32LE(executeFlag, 0); // Execute flag. buf.writeUInt32LE(entrypointAddress, 4); // Entry point address. - var dataPacket = buildCommand(COMMAND_MEM_END, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); // Drain the transmit queue. + await buildAndSendRequest(COMMAND_MEM_END, buf); await response.getResponse(); // Wait for a response. resolve(); // Resolve the promise. }); @@ -577,9 +618,7 @@ function MemCommands() { console.log("| MEM_DATA |"); console.log("+----------+"); await flush(); // Discard any untransmitted and unreceived data. // End the SLIP communication. - var dataPacket = buildCommand(COMMAND_MEM_DATA, buf2, calculateChecksum(data)); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); // Drain the transmit queue. + await buildAndSendRequest(COMMAND_MEM_DATA, buf2, calculateChecksum(data)); sequenceNumber ++; // Increment the sequence number. sizeWritten += sizeToWrite; // Updae the size of the data that we have now written. // Ensure that all the transmitted data is now outbound. await response.getResponse(); // Await a response. @@ -615,12 +654,17 @@ function MemCommands() { }; } // MemCommands + function portWrite(data) { console.log("Writing data down port, length: %d", data.length) return new Promise((resolve, reject) => { - port.write(data, ()=> {resolve()}); + port.write(data, async ()=> { + await drain; + resolve() + }); }); -} +} // portWrite + /** * Process the handling of flash commands. @@ -645,9 +689,7 @@ function FlashCommands() { buf.writeUInt32LE(Math.ceil(size/FLASH_PACKET_SIZE), 4); // Number of data packets to be sent. buf.writeUInt32LE(FLASH_PACKET_SIZE, 8); // Size of each data packet. buf.writeUInt32LE(address, 12); // Offset in memory to start writing. - var dataPacket = buildCommand(COMMAND_FLASH_BEGIN, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + await buildAndSendRequest(COMMAND_FLASH_BEGIN, buf); await response.getResponse(); resolve(); }); @@ -657,16 +699,16 @@ function FlashCommands() { async function flashData(data, sequenceNumber) { console.log(">> flashData: sequenceId: %d, size: %d", sequenceNumber, data.length); - if (data.length < 16*1024) { - var temp = Buffer.alloc(16*1024, 0xff); + if (data.length < FLASH_PACKET_SIZE) { + var temp = Buffer.alloc(FLASH_PACKET_SIZE, 0xff); data.copy(temp); data = temp; } var buf2 = Buffer.alloc(4*4 + data.length); // Allocate storage for the command preamble. buf2.writeUInt32LE(data.length, 0); // Size of data to be sent. buf2.writeUInt32LE(sequenceNumber, 4); // Current sequence number. - buf2.writeUInt32LE(0, 8); - buf2.writeUInt32LE(0, 12); + buf2.writeUInt32LE(0, 8); + buf2.writeUInt32LE(0, 12); data.copy(buf2, 16); //var buf = Buffer.concat([buf2, data]); //console.log("Here is the data we are sending:\n%s", hexy.hexy(buf)); @@ -676,10 +718,7 @@ function FlashCommands() { console.log("| FLASH_DATA |"); console.log("+------------+"); await flush(); // Discard any untransmitted and unreceived data. - var dataPacket = buildCommand(COMMAND_FLASH_DATA, buf2, calculateChecksum(data)); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); // Ensure that all the transmitted data is now outbound. - + await buildAndSendRequest(COMMAND_FLASH_DATA, buf2, calculateChecksum(data)); await response.getResponse(); // Await a response. resolve(); // Indicate that the command has been completed. }); @@ -701,9 +740,7 @@ function FlashCommands() { await flush(); // Discard any pending input or output. var buf = Buffer.allocUnsafe(4); // Allocate the payload which is one 32bit word. buf.writeUInt32LE(command, 0); // Execute flag. - var dataPacket = buildCommand(COMMAND_FLASH_END, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); // Drain the transmit queue. + await buildAndSendRequest(COMMAND_FLASH_END, buf); await response.getResponse(); // Wait for a response. resolve(); // Resolve the promise. }); @@ -734,7 +771,7 @@ function FlashCommands() { var sizeRemaining = totalSize - sizeWritten; // Calculate the amount of data still expected. var offsetIntoData = sequenceNumber * FLASH_PACKET_SIZE; // Calculate the offset into the data from which to send. var sizeToWrite = sizeRemaining > FLASH_PACKET_SIZE?FLASH_PACKET_SIZE:sizeRemaining; // Calculate how large this unit should be. - var tempBuf = Buffer.allocUnsafe(sizeToWrite); + var tempBuf = Buffer.allocUnsafe(sizeToWrite); data.copy(tempBuf, 0, offsetIntoData, offsetIntoData+sizeToWrite); await flashData(tempBuf, sequenceNumber); sequenceNumber++; @@ -803,17 +840,16 @@ function writeReg(address, value) { var buf = Buffer.allocUnsafe(4*4); buf.writeUInt32LE(address, 0); buf.writeUInt32LE(value, 4); - buf.writeUInt32LE(0xffffffff, 8); // Mask - buf.writeUInt32LE(0, 12); // Delay - var dataPacket = buildCommand(COMMAND_WRITE_REG, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + buf.writeUInt32LE(0xffffffff, 8); // Mask + buf.writeUInt32LE(0, 12); // Delay + await buildAndSendRequest(COMMAND_WRITE_REG, buf); await response.getResponse(); resolve(); }); return p; } // writeReg + /** * Set the SPI configuration parameters. * @param {Integer} size The size of SPI flash. @@ -828,20 +864,38 @@ function spiSetParams(size) { var p = new Promise(async function(resolve, reject) { await flush(); var buf = Buffer.allocUnsafe(6*4); - buf.writeUInt32LE(0, 0); // id - buf.writeUInt32LE(size, 4); // Total size - buf.writeUInt32LE(64*1024, 8); // block size - buf.writeUInt32LE(4*1024, 12); // sector size - buf.writeUInt32LE(256, 16); // page size - buf.writeUInt32LE(0xffff, 20); // status mask - var dataPacket = buildCommand(COMMAND_SPI_SET_PARAMS, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + buf.writeUInt32LE(0, 0); // id + buf.writeUInt32LE(size, 4); // Total size + buf.writeUInt32LE(64*1024, 8); // block size + buf.writeUInt32LE(4*1024, 12); // sector size + buf.writeUInt32LE(256, 16); // page size + buf.writeUInt32LE(0xffff, 20); // status mask + await buildAndSendRequest(COMMAND_SPI_SET_PARAMS, buf); await response.getResponse(); resolve(); }); return p; -} // writeReg +} // spiSetParams + + +/** + * Erase the flash memory. + * @returns A promise that is fulfilled when the flash memory has been erased. + */ +function eraseFlash() { + console.log("+-------------+"); + console.log("| ERASE_FLASH |"); + console.log("+-------------+"); + console.log(">> Erase Flash: Command: 0x%s", COMMAND_ERASE_FLASH.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(0); + await buildAndSendRequest(COMMAND_ERASE_FLASH, buf); + await response.getResponse(); + resolve(); + }); + return p; +} // spiSetParams /** @@ -855,9 +909,7 @@ function readReg(address) { await flush(); var buf = Buffer.allocUnsafe(4); buf.writeUInt32LE(address, 0); - var dataPacket = buildCommand(COMMAND_READ_REG, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); + await buildAndSendRequest(COMMAND_READ_REG, buf); var result = await response.getResponse(); console.log("readReg: %O", result); resolve(result); @@ -874,20 +926,21 @@ function readReg(address) { * @returns The MD5 hash value. */ function spiFlashMD5(address, size) { + console.log("+---------------+"); + console.log("| SPI_FLASH_MD5 |"); + console.log("+---------------+"); console.log(">> spiFlashMD5: Command: 0x%s, Address: 0x%s, Size: %d", COMMAND_SPI_FLASH_MD5.toString(16), address.toString(16), size); var p = new Promise(async function(resolve, reject) { await flush(); var buf = Buffer.allocUnsafe(4 * 4); buf.writeUInt32LE(address, 0); - buf.writeUInt32LE(size, 0); - buf.writeUInt32LE(0, 0); - buf.writeUInt32LE(0, 0); - var dataPacket = buildCommand(COMMAND_SPI_FLASH_MD5, buf); - await portWrite(dataPacket.getSLIPPacket()); - await drain(); - var result = await response.getResponse(); - console.log("spiFlashMD5 result: %O", result); - resolve(result); + buf.writeUInt32LE(size, 4); + buf.writeUInt32LE(0, 8); + buf.writeUInt32LE(0, 12); + await buildAndSendRequest(COMMAND_SPI_FLASH_MD5, buf); + await response.getResponse(); + console.log("spiFlashMD5 result:\n%s", hexy.hexy(response.getData())); + resolve(response.getData()); }); return p; } // spiFlashMD5 @@ -946,9 +999,10 @@ function enterDownloadMode() { async function flashFile(address, fileName) { console.log("Flashing file %s to address 0x%s", fileName, address.toString(16)); var fileData = fs.readFileSync(fileName); - console.log("Read file ... size is %d", fileData.length); + console.log("Read file ... size is %d, md5: %s", fileData.length, md5(fileData)); await FlashCommands().send(fileData, address); console.log("Flash File complete"); + return fileData.length; } // flashFile @@ -1015,7 +1069,9 @@ port.open(async (err)=>{ i++; var fileName = argv._[i]; // Obtain the file name. i++; - await flashFile(address, fileName); // Invoke the processor to upload the file into flash. + var size = await flashFile(address, fileName); // Invoke the processor to upload the file into flash. + console.log("File size written: %d", size); + await spiFlashMD5(address, size); } // End of process each file. From 2ae46aa1353bc0e2ffd75c8842c48ec18d999a32 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Tue, 17 Apr 2018 23:02:57 -0500 Subject: [PATCH 199/310] Upload of sample flasher --- tools/esptool_libs/nodejs/SampleFlasher/flasher_stub.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tools/esptool_libs/nodejs/SampleFlasher/flasher_stub.json diff --git a/tools/esptool_libs/nodejs/SampleFlasher/flasher_stub.json b/tools/esptool_libs/nodejs/SampleFlasher/flasher_stub.json new file mode 100644 index 00000000..248949cf --- /dev/null +++ b/tools/esptool_libs/nodejs/SampleFlasher/flasher_stub.json @@ -0,0 +1,7 @@ +{ + "textAddress": "40090000", + "dataAddress": "3ffd2ba4", + "entryPoint": "40090564", + "textData": "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", + "dataData": "CAD8Pw==" +} From 6817fee198f017f746ea7fc2c283f8a38bd01a7b Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Mon, 23 Apr 2018 16:30:55 -0700 Subject: [PATCH 200/310] Added GeneralUtils::wifiErrorToString - Convert a wifi_err_reason_t code to a string --- cpp_utils/GeneralUtils.cpp | 76 ++++++++++++++++++++++++++++++++++++++ cpp_utils/GeneralUtils.h | 1 + 2 files changed, 77 insertions(+) diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index ccf74f79..960f3172 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -434,6 +434,82 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "Unknown ESP_ERR error"; } // errorToString +/** + * @brief Convert a wifi_err_reason_t code to a string. + * @param [in] errCode The errCode to be converted. + * @return A string representation of the error code. + * + * @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h. + */ +const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { + if (errCode == ESP_OK) + return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)"; + if (errCode == UINT8_MAX) + return "Not Connected (default value)"; + + switch((wifi_err_reason_t) errCode) { + case WIFI_REASON_UNSPECIFIED: + return "WIFI_REASON_UNSPECIFIED"; + case WIFI_REASON_AUTH_EXPIRE: + return "WIFI_REASON_AUTH_EXPIRE"; + case WIFI_REASON_AUTH_LEAVE: + return "WIFI_REASON_AUTH_LEAVE"; + case WIFI_REASON_ASSOC_EXPIRE: + return "WIFI_REASON_ASSOC_EXPIRE"; + case WIFI_REASON_ASSOC_TOOMANY: + return "WIFI_REASON_ASSOC_TOOMANY"; + case WIFI_REASON_NOT_AUTHED: + return "WIFI_REASON_NOT_AUTHED"; + case WIFI_REASON_NOT_ASSOCED: + return "WIFI_REASON_NOT_ASSOCED"; + case WIFI_REASON_ASSOC_LEAVE: + return "WIFI_REASON_ASSOC_LEAVE"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "WIFI_REASON_ASSOC_NOT_AUTHED"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "WIFI_REASON_DISASSOC_PWRCAP_BAD"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "WIFI_REASON_DISASSOC_SUPCHAN_BAD"; + case WIFI_REASON_IE_INVALID: + return "WIFI_REASON_IE_INVALID"; + case WIFI_REASON_MIC_FAILURE: + return "WIFI_REASON_MIC_FAILURE"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "WIFI_REASON_IE_IN_4WAY_DIFFERS"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "WIFI_REASON_GROUP_CIPHER_INVALID"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "WIFI_REASON_PAIRWISE_CIPHER_INVALID"; + case WIFI_REASON_AKMP_INVALID: + return "WIFI_REASON_AKMP_INVALID"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "WIFI_REASON_UNSUPP_RSN_IE_VERSION"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "WIFI_REASON_INVALID_RSN_IE_CAP"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "WIFI_REASON_802_1X_AUTH_FAILED"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "WIFI_REASON_CIPHER_SUITE_REJECTED"; + + case WIFI_REASON_BEACON_TIMEOUT: + return "WIFI_REASON_BEACON_TIMEOUT"; + case WIFI_REASON_NO_AP_FOUND: + return "WIFI_REASON_NO_AP_FOUND"; + case WIFI_REASON_AUTH_FAIL: + return "WIFI_REASON_AUTH_FAIL"; + case WIFI_REASON_ASSOC_FAIL: + return "WIFI_REASON_ASSOC_FAIL"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_HANDSHAKE_TIMEOUT"; + } + return "Unknown ESP_ERR error"; +} // wifiErrorToString + + /** * @brief Convert a string to lower case. diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 013953dc..3706040e 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -23,6 +23,7 @@ class GeneralUtils { static void dumpInfo(); static bool endsWith(std::string str, char c); static const char* errorToString(esp_err_t errCode); + static const char* wifiErrorToString(uint8_t value); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); static std::vector split(std::string source, char delimiter); From 6014b063810b3cd53a8dc3b3f2c8e582259d2b79 Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Mon, 23 Apr 2018 16:37:15 -0700 Subject: [PATCH 201/310] Modified WiFi::connectAP so that it returns ESP_OK if successfully receives a SYSTEM_EVENT_STA_GOT_IP event. Otherwise returns wifi_err_reason_t --- cpp_utils/WiFi.cpp | 19 ++++++++++--------- cpp_utils/WiFi.h | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index bfe9477d..36590314 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -55,7 +55,7 @@ WiFi::WiFi() m_eventLoopStarted = false; m_initCalled = false; //m_pWifiEventHandler = new WiFiEventHandler(); - m_apConnected = false; // Are we connected to an access point? + m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? } // WiFi @@ -153,12 +153,12 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @param [in] ssid The network SSID of the access point to which we wish to connect. * @param [in] password The password of the access point to which we wish to connect. * @param [in] waitForConnection Block until the connection has an outcome. - * @return N/A. + * @returns ESP_OK if successfully connected to an access point. Otherwise returns wifi_err_reason_t - use GeneralUtils::wifiErrorToString(uint8_t errCode) to print the error. */ -bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ ESP_LOGD(LOG_TAG, ">> connectAP"); - m_apConnected = false; + m_apConnectionStatus = UINT8_MAX; init(); if (ip != 0 && gw != 0 && netmask != 0) { @@ -206,7 +206,7 @@ bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool m_connectFinished.give(); ESP_LOGD(LOG_TAG, "<< connectAP"); - return m_apConnected; // Return true if we are now connected and false if not. + return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. } // connectAP @@ -228,7 +228,7 @@ void WiFi::dump() { * @brief Returns whether wifi is connected to an access point */ bool WiFi::isConnectedToAP() { - return m_apConnected; + return m_apConnectionStatus; } // isConnected @@ -255,10 +255,11 @@ bool WiFi::isConnectedToAP() { // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that // indicates we are waiting for a connection complete. if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the flag. - pWiFi->m_apConnected = true; + + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. + pWiFi->m_apConnectionStatus = ESP_OK; } else { - pWiFi->m_apConnected = false; + pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; } pWiFi->m_connectFinished.give(); } diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index e12994ef..a57360ca 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -116,7 +116,7 @@ class WiFi { uint8_t m_dnsCount=0; bool m_eventLoopStarted; bool m_initCalled; - bool m_apConnected; // Are we connected to an access point? + uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); public: @@ -130,7 +130,7 @@ class WiFi { void setDNSServer(int numdns, ip_addr_t ip); struct in_addr getHostByName(const std::string& hostName); struct in_addr getHostByName(const char* hostName); - bool connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); void dump(); bool isConnectedToAP(); static std::string getApMac(); From 2438e6d2bcf8216101a1800412696f5f8d78074f Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Mon, 23 Apr 2018 16:37:44 -0700 Subject: [PATCH 202/310] Modified WiFi::connectAP so that it returns ESP_OK if successfully receives a SYSTEM_EVENT_STA_GOT_IP event. Otherwise returns wifi_err_reason_t --- cpp_utils/WiFi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 36590314..503e9235 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -153,7 +153,7 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @param [in] ssid The network SSID of the access point to which we wish to connect. * @param [in] password The password of the access point to which we wish to connect. * @param [in] waitForConnection Block until the connection has an outcome. - * @returns ESP_OK if successfully connected to an access point. Otherwise returns wifi_err_reason_t - use GeneralUtils::wifiErrorToString(uint8_t errCode) to print the error. + * @returns ESP_OK if successfully receives a SYSTEM_EVENT_STA_GOT_IP event. Otherwise returns wifi_err_reason_t - use GeneralUtils::wifiErrorToString(uint8_t errCode) to print the error. */ uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ ESP_LOGD(LOG_TAG, ">> connectAP"); From af664f01e706dce7c781f660d867eff8f8c83000 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Mon, 23 Apr 2018 19:34:36 -0500 Subject: [PATCH 203/310] Implementation of async BLEScan #496 --- cpp_utils/BLEScan.cpp | 15 ++++- cpp_utils/BLEScan.h | 3 +- cpp_utils/tests/BLETests/SampleAsyncScan.cpp | 58 ++++++++++++++++++++ cpp_utils/tests/BLETests/main.cpp | 2 + tools/bootloaderExamine/.gitignore | 2 + 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 cpp_utils/tests/BLETests/SampleAsyncScan.cpp create mode 100644 tools/bootloaderExamine/.gitignore diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index e450664e..73086c6e 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -74,6 +74,9 @@ void BLEScan::handleGAPEvent( // asked to stop. case ESP_GAP_SEARCH_INQ_CMPL_EVT: { m_stopped = true; + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } m_semaphoreScanEnd.give(); break; } // ESP_GAP_SEARCH_INQ_CMPL_EVT @@ -186,10 +189,14 @@ void BLEScan::setWindow(uint16_t windowMSecs) { /** * @brief Start scanning. * @param [in] duration The duration in seconds for which to scan. - * @return N/A. + * @param [in] scanCompleteCB A function to be called when scanning has completed. This can + * be supplied as nullptr (the default) in which case the call to start will block until scanning has + * been completed. + * @return The BLEScanResults. Only applicable if we are waiting for results. */ -BLEScanResults BLEScan::start(uint32_t duration) { +BLEScanResults BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. m_semaphoreScanEnd.take(std::string("start")); @@ -213,7 +220,9 @@ BLEScanResults BLEScan::start(uint32_t duration) { m_stopped = false; - m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + if (m_scanCompleteCB == nullptr) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } ESP_LOGD(LOG_TAG, "<< start()"); return m_scanResults; diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index c905c436..3e53ce64 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -53,7 +53,7 @@ class BLEScan { bool wantDuplicates = false); void setInterval(uint16_t intervalMSecs); void setWindow(uint16_t windowMSecs); - BLEScanResults start(uint32_t duration); + BLEScanResults start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults) = nullptr); void stop(); private: @@ -71,6 +71,7 @@ class BLEScan { FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); BLEScanResults m_scanResults; bool m_wantDuplicates; + void (*m_scanCompleteCB)(BLEScanResults scanResults); }; // BLEScan #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/tests/BLETests/SampleAsyncScan.cpp b/cpp_utils/tests/BLETests/SampleAsyncScan.cpp new file mode 100644 index 00000000..2973b652 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleAsyncScan.cpp @@ -0,0 +1,58 @@ +/** + * Perform an async scanning for BLE advertised servers. + */ +#include "BLEUtils.h" +#include "BLEScan.h" +#include + +#include "BLEDevice.h" +#include "BLEAdvertisedDevice.h" +#include "sdkconfig.h" + +/** + * Callback for each detected advertised device. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice advertisedDevice) { + printf("Advertised Device: %s\n", advertisedDevice.toString().c_str()); + } +}; + + +/** + * Callback invoked when scanning has completed. + */ +static void scanCompleteCB(BLEScanResults scanResults) { + printf("Scan complete!\n"); + printf("We found %d devices\n", scanResults.getCount()); + scanResults.dump(); +} // scanCompleteCB + +/** + * Run the sample. + */ +static void run() { + printf("Async Scanning sample starting\n"); + BLEDevice::init(""); + + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false); + pBLEScan->setActiveScan(true); + printf("About to start scanning for 10 seconds\n"); + pBLEScan->start(10, scanCompleteCB); + printf("Now scanning in the background ... scanCompleteCB() will be called when done.\n"); + + // + // Now going into a loop logging that we are still alive. + // + while(1) { + printf("Tick! - still alive\n"); + FreeRTOS::sleep(1000); + } + printf("Scanning sample ended\n"); +} + +void SampleAsyncScan(void) +{ + run(); +} // app_main diff --git a/cpp_utils/tests/BLETests/main.cpp b/cpp_utils/tests/BLETests/main.cpp index 732ae16d..77c3567b 100644 --- a/cpp_utils/tests/BLETests/main.cpp +++ b/cpp_utils/tests/BLETests/main.cpp @@ -9,6 +9,7 @@ extern "C" { // The list of sample entry points. void Sample_MLE_15(void); void Sample1(void); +void SampleAsyncScan(void); void SampleClient(void); void SampleClient_Notify(void); void SampleClientAndServer(void); @@ -27,6 +28,7 @@ void SampleWrite(void); void app_main(void) { //Sample_MLE_15(); //Sample1(); + //SampleAsyncScan(); //SampleClient(); //SampleClient_Notify(); //SampleClientAndServer(); diff --git a/tools/bootloaderExamine/.gitignore b/tools/bootloaderExamine/.gitignore new file mode 100644 index 00000000..5db6b6fc --- /dev/null +++ b/tools/bootloaderExamine/.gitignore @@ -0,0 +1,2 @@ +/a.out +/app-template.bin From 0eda8c66b20639e9d7f1cc62dd42f8ea594923a0 Mon Sep 17 00:00:00 2001 From: Jeff Edson Date: Mon, 23 Apr 2018 17:57:02 -0700 Subject: [PATCH 204/310] Prior to this change, BootWiFi::boot would hang on m_completeSemaphore.wait("boot") if WiFi::connectAP received SYSTEM_EVENT_STA_DISCONNECTED. This commit changes 2 items 1) after calling m_wifi.connectAP, calls m_completeSemaphore.give() to ensure that we dont hang 2) BootWiFi::boot returns ESP_OK if it successfully receives a SYSTEM_EVENT_STA_GOT_IP event. Otherwise it returns the wifi_err_reason_t so that the programmer can then handle it in a distinct manner. --- networking/bootwifi/BootWiFi.cpp | 24 +++++++++++++++++------- networking/bootwifi/BootWiFi.h | 3 ++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index 8a62786a..cc07c898 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -243,6 +243,7 @@ class BootWifiEventHandler: public WiFiEventHandler { esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { ESP_LOGD("BootWifiEventHandler", ">> staGotIP"); + m_pBootWiFi->m_apConnectionStatus = ESP_OK; // Set the status to ESP_OK m_pBootWiFi->m_completeSemaphore.give(); // If we got an IP address, then we can end the boot process. ESP_LOGD("BootWifiEventHandler", "<< staGotIP"); return ESP_OK; @@ -255,6 +256,11 @@ class BootWifiEventHandler: public WiFiEventHandler { /** * Boot WiFi + * + * @brief Get connected to WiFi + * + * @detailed If SSID & Password were previously saved, connect to the AP. Otherwise become an AP and start an HTTP server so that the user can set SSID & Password - then save it. + * */ void BootWiFi::bootWiFi2() { ESP_LOGD(LOG_TAG, ">> bootWiFi2"); @@ -286,10 +292,8 @@ void BootWiFi::bootWiFi2() { connectionInfo.ipInfo.netmask.addr ); - // Connect to the access point. - while(!m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password)){ - ESP_LOGE(LOG_TAG, "Unable to connect to access point \"%s\" - trying again...", connectionInfo.ssid); - }; + m_apConnectionStatus = m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password); // Try to connect to the access point. + m_completeSemaphore.give(); // end the boot process so we don't hang... } else { // We do NOT have connection information. Let us now become an access @@ -316,22 +320,28 @@ void BootWiFi::setAccessPointCredentials(std::string ssid, std::string password) /** - * @brief Main entry point into booting WiFi + * @brief Main entry point into booting WiFi - see BootWiFi2 for more detail. + * + * The event handler will be called back with the outcome of the connection. + * + * @returns ESP_OK if successfully connected to an access point. Otherwise returns wifi_err_reason_t - to print use GeneralUtils::wifiErrorToString */ -void BootWiFi::boot() { +uint8_t BootWiFi::boot() { ESP_LOGD(LOG_TAG, ">> boot"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " | BootWiFi |"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " Access point credentials: %s/%s", m_ssid.c_str(), m_password.c_str()); - m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. + m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. bootWiFi2(); m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. m_wifi.setWifiEventHandler(nullptr); // Remove the WiFi boot handler when we have completed booting. ESP_LOGD(LOG_TAG, "<< boot"); + return m_apConnectionStatus; } // boot BootWiFi::BootWiFi() { m_httpServerStarted = false; + m_apConnectionStatus = UINT8_MAX; setAccessPointCredentials("esp32", "password"); // Default access point credentials } diff --git a/networking/bootwifi/BootWiFi.h b/networking/bootwifi/BootWiFi.h index 3d37dccd..9c4a17b6 100644 --- a/networking/bootwifi/BootWiFi.h +++ b/networking/bootwifi/BootWiFi.h @@ -24,12 +24,13 @@ class BootWiFi { bool m_httpServerStarted; std::string m_ssid; std::string m_password; + uint8_t m_apConnectionStatus; // receives the connection status. ESP_OK = received SYSTEM_EVENT_STA_GOT_IP event. FreeRTOS::Semaphore m_completeSemaphore = FreeRTOS::Semaphore("completeSemaphore"); public: BootWiFi(); void setAccessPointCredentials(std::string ssid, std::string password); - void boot(); + uint8_t boot(); }; #endif /* MAIN_BOOTWIFI_H_ */ From 5ae9b98309030d8fedde4addcea2f9d2cdfff4ed Mon Sep 17 00:00:00 2001 From: chegewara Date: Fri, 27 Apr 2018 13:18:27 +0200 Subject: [PATCH 205/310] Add retrieving raw advertising data --- cpp_utils/BLEAdvertisedDevice.cpp | 7 +++++++ cpp_utils/BLEAdvertisedDevice.h | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 351c5e10..67603dfb 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -234,6 +234,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { uint8_t ad_type; uint8_t sizeConsumed = 0; bool finished = false; + setPayload(payload); while(!finished) { length = *payload; // Retrieve the length of the record. @@ -506,7 +507,13 @@ std::string BLEAdvertisedDevice::toString() { return ss.str(); } // toString +uint8_t* BLEAdvertisedDevice::getPayload() { + return m_payload; +} +void BLEAdvertisedDevice::setPayload(uint8_t* payload) { + m_payload = payload; +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index 41bc4c66..a3b1e6e2 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -39,9 +39,10 @@ class BLEAdvertisedDevice { BLEUUID getServiceDataUUID(); BLEUUID getServiceUUID(); int8_t getTXPower(); + uint8_t* getPayload(); - bool isAdvertisingService(BLEUUID uuid); + bool isAdvertisingService(BLEUUID uuid); bool haveAppearance(); bool haveManufacturerData(); bool haveName(); @@ -69,6 +70,7 @@ class BLEAdvertisedDevice { void setServiceUUID(const char* serviceUUID); void setServiceUUID(BLEUUID serviceUUID); void setTXPower(int8_t txPower); + void setPayload(uint8_t* payload); bool m_haveAppearance; @@ -92,6 +94,7 @@ class BLEAdvertisedDevice { int8_t m_txPower; std::string m_serviceData; BLEUUID m_serviceDataUUID; + uint8_t* m_payload; }; /** From e00a6c79cdad38bec2a64c07d7ab6a7a9cfed62e Mon Sep 17 00:00:00 2001 From: mws-rmain <30533684+mws-rmain@users.noreply.github.com> Date: Fri, 27 Apr 2018 10:13:26 -0400 Subject: [PATCH 206/310] Update PubSubClient.cpp --- cpp_utils/PubSubClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/PubSubClient.cpp b/cpp_utils/PubSubClient.cpp index e3542cb0..8aaca5eb 100644 --- a/cpp_utils/PubSubClient.cpp +++ b/cpp_utils/PubSubClient.cpp @@ -315,7 +315,7 @@ bool PubSubClient::connect(){ ESP_LOGD(TAG, "Connect to mqtt server..."); - ESP_LOGD(TAG, "ip: %s port: %d", _config.ip.c_str(), _config.port) + ESP_LOGD(TAG, "ip: %s port: %d", _config.ip.c_str(), _config.port); int result = _client->connect((char *)_config.ip.c_str(), _config.port); if (result == 0) { From c1485348ef286b3452f068a86d731ec8db4b31a3 Mon Sep 17 00:00:00 2001 From: mws-rmain <30533684+mws-rmain@users.noreply.github.com> Date: Fri, 27 Apr 2018 10:18:15 -0400 Subject: [PATCH 207/310] Update BLEServer.cpp --- cpp_utils/BLEServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 9d26eb50..c33afdf2 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -18,7 +18,6 @@ #include "BLEUtils.h" #include #include -#include #include #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" From fac6baad4bf9e1a5f982bf568fda2a5929328dae Mon Sep 17 00:00:00 2001 From: mws-rmain <30533684+mws-rmain@users.noreply.github.com> Date: Fri, 27 Apr 2018 10:20:22 -0400 Subject: [PATCH 208/310] Update SockServ.cpp --- cpp_utils/SockServ.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 6d35a387..296b3a4b 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -64,7 +64,7 @@ SockServ::~SockServ() { SockServ* pSockServ = (SockServ*)data; try { while(1) { - ESP_LOGD(LOG_TAG, "Waiting on accept") + ESP_LOGD(LOG_TAG, "Waiting on accept"); Socket tempSock = pSockServ->m_serverSocket.accept(); if (!tempSock.isValid()) { continue; @@ -229,7 +229,7 @@ Socket SockServ::waitForData(std::set& socketSet) { * or can return immediately is there is already a client connection in existence. */ Socket SockServ::waitForNewClient() { - ESP_LOGD(LOG_TAG, ">> waitForNewClient") + ESP_LOGD(LOG_TAG, ">> waitForNewClient"); m_clientSemaphore.wait("waitForNewClient"); // Unlocked in acceptTask. m_clientSemaphore.take("waitForNewClient"); Socket tempSocket; From 7a33655165211b601c702b6705a708786333aa56 Mon Sep 17 00:00:00 2001 From: Benjamin Aigner Date: Mon, 7 May 2018 16:57:24 +0200 Subject: [PATCH 209/310] Removed vTaskDelete, avoiding crashes with Task::stop() called --- cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp index f0385c52..29bff125 100644 --- a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp +++ b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp @@ -59,7 +59,6 @@ class MyTask : public Task { } vTaskDelay(2000/portTICK_PERIOD_MS); // simulate write message every 2 seconds } - vTaskDelete(NULL); } }; From 2a7358ce2930fe255eb8988a8b23a8a7206c17be Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 13 May 2018 18:58:11 -0500 Subject: [PATCH 210/310] Initial implementation of FTP Server --- cpp_utils/FTPCallbacks.cpp | 155 +++++++ cpp_utils/FTPServer.cpp | 823 +++++++++++++++++++++++++++++++++++++ cpp_utils/FTPServer.h | 137 ++++++ cpp_utils/GPIO.cpp | 1 + 4 files changed, 1116 insertions(+) create mode 100644 cpp_utils/FTPCallbacks.cpp create mode 100644 cpp_utils/FTPServer.cpp create mode 100644 cpp_utils/FTPServer.h diff --git a/cpp_utils/FTPCallbacks.cpp b/cpp_utils/FTPCallbacks.cpp new file mode 100644 index 00000000..d88c2300 --- /dev/null +++ b/cpp_utils/FTPCallbacks.cpp @@ -0,0 +1,155 @@ +#include "FTPServer.h" +#include +#include +#include +#include + +static const char* LOG_TAG = "FTPCallbacks"; +/** + * Called at the start of a STOR request. The file name is the name of the file the client would like to + * save. + */ +void FTPFileCallbacks::onStoreStart(std::string fileName) { + ESP_LOGD(LOG_TAG, ">> FTPFileCallbacks::onStoreStart: fileName=%s", fileName.c_str()); + m_storeFile.open(fileName, std::ios::binary); // Open the file for writing. + if (m_storeFile.fail()) { + throw FTPServer::FileException(); + } + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreStart"); +} // FTPFileCallbacks#onStoreStart + + +/** + * Called when the client presents a new chunk of data to be saved. + */ +size_t FTPFileCallbacks::onStoreData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onStoreData: size=%d", size); + m_storeFile.write((char *)data, size); // Store data received. + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreData: size=%d", size); + return size; +} // FTPFileCallbacks#onStoreData + + +/** + * Called at the end of a STOR request. This indicates that the client has completed its transmission of the + * file. + */ +void FTPFileCallbacks::onStoreEnd() { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onStoreEnd"); + m_storeFile.close(); // Close the open file. + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreEnd"); +} // FTPFileCallbacks#onStoreEnd + + +/** + * Called when the client requests retrieval of a file. + */ +void FTPFileCallbacks::onRetrieveStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveStart: fileName=%s", fileName.c_str()); + m_byteCount = 0; + m_retrieveFile.open(fileName, std::ios::binary); + if (m_retrieveFile.fail()) { + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveStart: ***FileException***"); + throw FTPServer::FileException(); + } + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveStart"); +} // FTPFileCallbacks#onRetrieveStart + + +/** + * Called when the client is ready to receive the next piece of the file. To indicate that there + * is no more data to send, return a size of 0. + * @param data The data buffer that we can fill to return data back to the client. + * @param size The maximum size of the data buffer that we can populate. + * @return The size of data being returned. Return 0 to indicate that there is no more data to return. + */ +size_t FTPFileCallbacks::onRetrieveData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveData"); + m_retrieveFile.read((char *)data, size); + size_t readSize = m_retrieveFile.gcount(); + m_byteCount += readSize; + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveData: sizeRead=%d", readSize); + return m_retrieveFile.gcount(); // Return the number of bytes read. +} // FTPFileCallbacks#onRetrieveData + + +/** + * Called when the retrieval has been completed. + */ +void FTPFileCallbacks::onRetrieveEnd() { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveEnd"); + m_retrieveFile.close(); + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveEnd: bytesTransmitted=%d", m_byteCount); +} // FTPFileCallbacks#onRetrieveEnd + + +/** + * Return a list of files in the file system. + * @return a list of files in the file system. + */ +std::string FTPFileCallbacks::onDir() { + + DIR* dir = opendir(FTPServer::getCurrentDirectory().c_str()); + std::stringstream ss; + while(1) { + struct dirent* pDirentry = readdir(dir); + if (pDirentry == nullptr) { + break; + } + ss << pDirentry->d_name << "\r\n"; + } + closedir(dir); + return ss.str(); +} // FTPFileCallbacks#onDir + + +/// ---- END OF FTPFileCallbacks + + +void FTPCallbacks::onStoreStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreStart: fileName=%s", fileName.c_str()); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreStart"); +} // FTPCallbacks#onStoreStart + + +size_t FTPCallbacks::onStoreData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreData: size=%d", size); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreData"); + return 0; +} // FTPCallbacks#onStoreData + + +void FTPCallbacks::onStoreEnd() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreEnd"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreEnd"); +} // FTPCallbacks#onStoreEnd + + +void FTPCallbacks::onRetrieveStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveStart"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveStart"); +} // FTPCallbacks#onRetrieveStart + + +size_t FTPCallbacks::onRetrieveData(uint8_t *data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveData"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveData: 0"); + return 0; +} // FTPCallbacks#onRetrieveData + + +void FTPCallbacks::onRetrieveEnd() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveEnd"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveEnd"); +} // FTPCallbacks#onRetrieveEnd + + +std::string FTPCallbacks::onDir() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onDir"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onDir"); + return ""; +} // FTPCallbacks#onDir + +FTPCallbacks::~FTPCallbacks() { + +} // FTPCallbacks#~FTPCallbacks diff --git a/cpp_utils/FTPServer.cpp b/cpp_utils/FTPServer.cpp new file mode 100644 index 00000000..f8d006fd --- /dev/null +++ b/cpp_utils/FTPServer.cpp @@ -0,0 +1,823 @@ +/* + * FTPServer.cpp + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#include "FTPServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* LOG_TAG = "FTPServer"; + +// trim from start (in place) +static void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} // ltrim + + +// trim from end (in place) +static void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} // rtrim + + +// trim from both ends (in place) +static void trim(std::string &s) { + ltrim(s); + rtrim(s); +} // trim + + +FTPServer::FTPServer() { + ESP_LOGD(LOG_TAG,">> FTPServer()"); + + m_dataSocket = -1; + m_clientSocket = -1; + m_dataPort = -1; + m_dataIp = -1; + m_passiveSocket = -1; + m_serverSocket = -1; + + m_callbacks = nullptr; + m_isPassive = false; + m_isImage = true; + m_chunkSize = 4096; + m_port = 21; // The default Server-PI port + m_loginRequired = false; + m_isAuthenticated = false; + m_userid = ""; + m_password = ""; + + ESP_LOGD(LOG_TAG,"<< FTPServer()"); +} // FTPServer#FTPServer + + +FTPServer::~FTPServer() { + // Nothing to do here by default. +} // FTPServer#~FTPServer + + +/** + * Close the connection to the FTP client. + */ +void FTPServer::closeConnection() { + ESP_LOGD(LOG_TAG,">> closeConnection"); + close(m_clientSocket); + ESP_LOGD(LOG_TAG,"<< closeConnection"); +} // FTPServer#closeConnection + + +/** + * Close a previously opened data connection. + */ +void FTPServer::closeData() { + ESP_LOGD(LOG_TAG,">> closeData"); + close(m_dataSocket); + m_dataSocket = -1; + ESP_LOGD(LOG_TAG,"<< closeData"); +} // FTPServer#closeData + + +/** + * Close the passive listening socket that was opened by listenPassive. + */ +void FTPServer::closePassive() { + ESP_LOGD(LOG_TAG,">> closePassive"); + close(m_passiveSocket); + m_passiveSocket = -1; + ESP_LOGD(LOG_TAG, "<< closePassive"); +} // FTPServer#closePassive + + +/** + * Retrieve the current directory. + */ +/* STATIC */ std::string FTPServer::getCurrentDirectory() { + char maxDirectory[256]; + std::string currentDirectory = getcwd(maxDirectory, sizeof(maxDirectory)); + return currentDirectory; +} // FTPServer#getCurrentDirectory + + +/** + * Create a listening socket for the new passive connection. + * @return a String for the passive parameters. + */ +std::string FTPServer::listenPassive() { + ESP_LOGD(LOG_TAG, ">> listenPassive"); + + m_passiveSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_passiveSocket == -1) { + ESP_LOGD(LOG_TAG, "socket: %s", strerror(errno)); + } + + struct sockaddr_in clientAddrInfo; + unsigned int addrInfoSize = sizeof(clientAddrInfo); + getsockname(m_clientSocket, (struct sockaddr*)&clientAddrInfo, &addrInfoSize); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(0); + int rc = bind(m_passiveSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "bind: %s", strerror(errno)); + } + + rc = listen(m_passiveSocket, 5); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "listen: %s", strerror(errno)); + } + + unsigned int addrLen = sizeof(serverAddress); + rc = getsockname(m_passiveSocket, (struct sockaddr*)&serverAddress, &addrLen); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "getsockname: %s", strerror(errno)); + } + + + std::stringstream ss; + ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 16) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 24) & 0xff) << + "," << ((serverAddress.sin_port >> 0) & 0xff) << + "," << ((serverAddress.sin_port >> 8) & 0xff); + std::string retStr = ss.str(); + + ESP_LOGD(LOG_TAG, "<< listenPassive: %s", retStr.c_str()); + return retStr; +} // FTPServer#listenPassive + + +/** + * Handle the AUTH command. + */ +void FTPServer::onAuth(std::istringstream& ss) { + std::string param; + ss >> param; + ESP_LOGD(LOG_TAG, ">> onAuth: %s", param.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + ESP_LOGD(LOG_TAG, "<< onAuth"); +} // FTPServer#onAuth + + +/** + * Change the current working directory. + * @param ss A string stream where the first parameter is the directory to change to. + */ +void FTPServer::onCwd(std::istringstream& ss) { + std::string path; + ss >> path; + ESP_LOGD(LOG_TAG, ">> onCwd: path=%s", path.c_str()); + chdir(path.c_str()); + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); + ESP_LOGD(LOG_TAG, "<< onCwd"); +} // FTPServer#onCwd + + +/** + * Process the client transmitted LIST request. + */ +void FTPServer::onList(std::istringstream& ss) { + std::string directory; + ss >> directory; + ESP_LOGD(LOG_TAG, ">> onList: directory=%s", directory.c_str()); + + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + if (m_callbacks != nullptr) { + std::string dirString = m_callbacks->onDir(); + sendData((uint8_t *)dirString.data(), dirString.length()); + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + ESP_LOGD(LOG_TAG, "<< onList"); +} // FTPServer#onList + + +void FTPServer::onMkd(std::istringstream &ss) { + std::string path; + ss >> path; + ESP_LOGD(LOG_TAG, ">> onMkd: path=%s", path.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onMkd"); +} // FTPServer#onMkd + + +/** + * Process a NOOP operation. + */ +void FTPServer::onNoop(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onNoop"); + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + ESP_LOGD(LOG_TAG, "<< onNoop"); +} // FTPServer#onNoop + + +/** + * Process PORT request. The information provided is encoded in the parameter as + * h1,h2,h3,h4,p1,p2 where h1,h2,h3,h4 is the IP address we should connect to + * and p1,p2 is the port number. The data is MSB + * + * Our logic does not form any connection but remembers the ip address and port number + * to be used for a subsequence data connection. + * + * Possible responses: + * 200 + * 500, 501, 421, 530 + */ +void FTPServer::onPort(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPort"); + char c; + uint16_t h1, h2, h3, h4, p1, p2; + ss >> h1 >> c >> h2 >> c >> h3 >> c >> h4 >> c >> p1 >> c >> p2; + m_dataPort = p1*256 + p2; + ESP_LOGD(LOG_TAG, "%d.%d.%d.%d %d", h1, h2, h3, h4, m_dataPort); + m_dataIp = h1<<24 | h2<<16 | h3<<8 | h4; + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + m_isPassive = false; + + ESP_LOGD(LOG_TAG, "<< onPort"); +} // FTPServer#onPort + + +/** + * Process the PASS command. + * Possible responses: + * 230 + * 202 + * 530 + * 500, 501, 503, 421 + * 332 + */ +void FTPServer::onPass(std::istringstream& ss) { + std::string password; + ss >> password; + ESP_LOGD(LOG_TAG, ">> onPass: password=%s", password.c_str()); + + // If the immediate last command wasn't USER then don't try and process PASS. + if (m_lastCommand != "USER") { + sendResponse(RESPONSE_503_BAD_SEQUENCE); + ESP_LOGD(LOG_TAG, "<< onPass"); + return; + } + + // Compare the supplied userid and passwords. + if (m_userid == m_suppliedUserid && password == m_password) { + sendResponse(RESPONSE_230_USER_LOGGED_IN); + m_isAuthenticated = true; + } else { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + closeConnection(); + m_isAuthenticated = false; + } + ESP_LOGD(LOG_TAG, "<< onPass"); +} // FTPServer#onPass + + +/** + * Process the PASV command. + * Possible responses: + * 227 + * 500, 501, 502, 421, 530 + */ +void FTPServer::onPasv(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPasv"); + std::string ipInfo = listenPassive(); + std::ostringstream responseTextSS; + responseTextSS << "Entering Passive Mode (" << ipInfo << ")."; + std::string responseText; + responseText = responseTextSS.str(); + sendResponse(RESPONSE_227_ENTERING_PASSIVE_MODE, responseText.c_str()); + m_isPassive = true; + + ESP_LOGD(LOG_TAG, "<< onPasv"); +} // FTPServer#onPasv + + +/** + * Process the PWD command to determine our current working directory. + * Possible responses: + * 257 + * 500, 501, 502, 421, 550 + */ +void FTPServer::onPWD(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPWD"); + sendResponse(257, "\"" + getCurrentDirectory() + "\""); + ESP_LOGD(LOG_TAG, "<< onPWD: %s", getCurrentDirectory().c_str()); +} // FTPServer#onPWD + + +/** + * Possible responses: + * 221 + * 500 + */ +void FTPServer::onQuit(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onQuit"); + sendResponse(FTPServer::RESPONSE_221_CLOSING_CONTROL_CONNECTION); // Service closing control connection. + closeConnection(); // Close the connection to the client. + ESP_LOGD(LOG_TAG, "<< onQuit"); +} // FTPServer#onQuit + + +/** + * Process a RETR command. The client sends this command to retrieve the content of a file. + * The name of the file is the first parameter in the input stream. + * + * Possible responses: + * 125, 150 + * (110) + * 226, 250 + * 425, 426, 451 + * 450, 550 + * 500, 501, 421, 530 + * @param ss The parameter stream. + */ +void FTPServer::onRetr(std::istringstream& ss) { + + // We open a data connection back to the client. We then invoke the callback to indicate that we have + // started a retrieve operation. We call the retrieve callback to request the next chunk of data and + // transmit this down the data connection. We repeat this until there is no more data to send at which + // point we close the data connection and we are done. + ESP_LOGD(LOG_TAG, ">> onRetr"); + std::string fileName; + + ss >> fileName; + uint8_t data[m_chunkSize]; + + + if (m_callbacks != nullptr) { + try { + m_callbacks->onRetrieveStart(fileName); + } catch(FTPServer::FileException& e) { + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + ESP_LOGD(LOG_TAG, "<< onRetr: Returned 550 to client."); + return; + } + } + + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + openData(); + if (m_callbacks != nullptr) { + int readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + while(readSize > 0) { + sendData(data, readSize); + readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + } + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + ESP_LOGD(LOG_TAG, "<< onRetr"); +} // FTPServer#onRetr + + +void FTPServer::onRmd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onRmd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onRmd"); +} // FTPServer#onRmd + + +/** + * Called to process a STOR request. This means that the client wishes to store a file + * on the server. The name of the file is found in the parameter. + */ +void FTPServer::onStor(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onStor"); + std::string fileName; + ss >> fileName; + + receiveFile(fileName); + ESP_LOGD(LOG_TAG, "<< onStor"); +} // FTPServer#onStor + + +void FTPServer::onSyst(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onSyst"); + sendResponse(215, "UNIX Type: L8"); + ESP_LOGD(LOG_TAG, "<< onSyst"); +} // FTPServer#onSyst + + +/** + * Process a TYPE request. The parameter that follows is the type of transfer we wish + * to process. Types include: + * I and A. + * + * Possible responses: + * 200 + * 500, 501, 504, 421, 530 + */ +void FTPServer::onType(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onType"); + std::string type; + ss >> type; + if (type.compare("I") == 0) { + m_isImage = true; + } else { + m_isImage = false; + } + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + ESP_LOGD(LOG_TAG, "<< onType: isImage=%d", m_isImage); +} // FTPServer#onType + + +/** + * Process a USER request. The parameter that follows is the identity of the user. + * + * Possible responses: + * 230 + * 530 + * 500, 501, 421 + * 331, 332 + * + */ +void FTPServer::onUser(std::istringstream& ss) { + // When we receive a user command, we next want to know if we should ask for a password. If the m_loginRequired + // flag is set then we do indeed want a password and will send the response that we wish one. + + std::string userName; + ss >> userName; + ESP_LOGD(LOG_TAG, ">> onUser: userName=%s", userName.c_str()); + if (m_loginRequired) { + sendResponse(FTPServer::RESPONSE_331_PASSWORD_REQUIRED); + } else { + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + } + m_suppliedUserid = userName; // Save the username that was supplied. + ESP_LOGD(LOG_TAG, "<< onUser"); +} // FTPServer#onUser + + +void FTPServer::onXmkd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onXmkd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onXmkd"); +} // FTPServer#onXmkd + + +void FTPServer::onXrmd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onXrmd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onXrmd"); +} // FTPServer#onXrmd + + +/** + * Open a data connection with the client. + * We will use closeData() to close the connection. + * @return True if the data connection succeeded. + */ +bool FTPServer::openData() { + if (m_isPassive) { + // Handle a passive connection ... here we receive a connection from the client from the passive socket. + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_dataSocket = accept(m_passiveSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + if (m_dataSocket == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::openData: accept(): %s", strerror(errno)); + closePassive(); + return false; + } + closePassive(); + } else { + // Handle an active connection ... here we connect to the client. + m_dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(m_dataIp); + serverAddress.sin_port = htons(m_dataPort); + + int rc = connect(m_dataSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::openData: connect(): %s", strerror(errno)); + return false; + } + } + return true; +} // FTPServer#openData + + +/** + * Process commands received from the client. + */ +void FTPServer::processCommand() { + sendResponse(FTPServer::RESPONSE_220_SERVICE_READY); // Service ready. + ESP_LOGD(LOG_TAG, ">> FTPServer::processCommand"); + m_lastCommand = ""; + while(1) { + std::string line = ""; + char currentChar; + char lastChar = '\0'; + int rc = recv(m_clientSocket, ¤tChar, 1, 0); + while(rc != -1 && rc!=0) { + line += currentChar; + if (lastChar == '\r' && currentChar == '\n') { + break; + } + //printf("%c\n", currentChar); + lastChar = currentChar; + rc = recv(m_clientSocket, ¤tChar, 1, 0); + } // End while we are waiting for a line. + + if (rc == 0 || rc == -1) { // If we didn't get a line or an error, then we have finished processing commands. + break; + } + + std::string command; + std::istringstream ss(line); + getline(ss, command, ' '); + trim(command); + + // We now have a command to process. + + ESP_LOGD(LOG_TAG, "Command: \"%s\"", command.c_str()); + if (command.compare("USER")==0) { + onUser(ss); + } + else if (command.compare("PASS")==0) { + onPass(ss); + } + else if (m_loginRequired && !m_isAuthenticated) { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + } + else if (command.compare("PASV")==0) { + onPasv(ss); + } + else if (command.compare("SYST")==0) { + onSyst(ss); + } + else if (command.compare("PORT")==0) { + onPort(ss); + } + else if (command.compare("LIST")==0) { + onList(ss); + } + else if (command.compare("TYPE")==0) { + onType(ss); + } + else if (command.compare("RETR")==0) { + onRetr(ss); + } + else if (command.compare("QUIT")==0) { + onQuit(ss); + } + else if (command.compare("AUTH")==0) { + onAuth(ss); + } + else if (command.compare("STOR")==0) { + onStor(ss); + } + else if (command.compare("PWD")==0) { + onPWD(ss); + } + else if (command.compare("MKD")==0) { + onMkd(ss); + } + else if (command.compare("XMKD")==0) { + onXmkd(ss); + } + else if (command.compare("RMD")==0) { + onRmd(ss); + } + else if (command.compare("XRMD")==0) { + onXrmd(ss); + } + else if (command.compare("CWD")==0) { + onCwd(ss); + } + else { + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + } + m_lastCommand = command; + } // End loop processing commands. + + close(m_clientSocket); // We won't be processing any further commands from this client. + ESP_LOGD(LOG_TAG, "<< FTPServer::processCommand"); +} // FTPServer::processCommand + + +/** + * Receive a file from the FTP client (STOR). The name of the file to be created is passed as a + * parameter. + */ +void FTPServer::receiveFile(std::string fileName) { + ESP_LOGD(LOG_TAG, ">> receiveFile: %s", fileName.c_str()); + if (m_callbacks != nullptr) { + try { + m_callbacks->onStoreStart(fileName); + } catch(FTPServer::FileException& e) { + ESP_LOGD(LOG_TAG, "Caught a file exception!"); + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + return; + } + } + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + uint8_t buf[m_chunkSize]; + uint32_t totalSizeRead = 0; + while(1) { + int rc = recv(m_dataSocket, &buf, m_chunkSize, 0); + if (rc <= 0) { + break; + } + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveData(buf, rc); + } + totalSizeRead += rc; + } + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + closeData(); + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + ESP_LOGD(LOG_TAG, "<< receiveFile: totalSizeRead=%d", totalSizeRead); +} // FTPServer#receiveFile + + +/** + * Send data to the client over the data connection previously opened with a call to openData(). + * @param pData A pointer to the data to send. + * @param size The number of bytes to send. + */ +void FTPServer::sendData(uint8_t* pData, uint32_t size) { + ESP_LOGD(LOG_TAG, ">> FTPServer::sendData: size=%d", size); + int rc = send(m_dataSocket, pData, size, 0); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::sendData: send(): %s", strerror(errno)); + } + ESP_LOGD(LOG_TAG, "<< FTPServer::sendData"); +} // FTPServer#sendData + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. + */ +void FTPServer::sendResponse(int code, std::string text) { + ESP_LOGD(LOG_TAG, ">> sendResponse: (%d) %s", code, text.c_str()); + std::ostringstream ss; + ss << code << " " << text << "\r\n"; + int rc = send(m_clientSocket, ss.str().data(), ss.str().length(), 0); + if (rc == -1) { + ESP_LOGE(LOG_TAG,"send: %s", strerror(errno)); + } + ESP_LOGD(LOG_TAG, "<< sendResponse"); +} // FTPServer#sendResponse + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. In this function, a standard piece of text is used based on + * the code. + */ +void FTPServer::sendResponse(int code) { + std::string text = "unknown"; + + switch(code) { // Map the code to a text string. + case RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION: + text = "File status okay; about to open data connection."; + break; + case RESPONSE_200_COMMAND_OK: + text = "Command okay."; + break; + case RESPONSE_220_SERVICE_READY: + text = "Service ready."; + break; + case RESPONSE_221_CLOSING_CONTROL_CONNECTION: + text = "Service closing control connection."; + break; + case RESPONSE_226_CLOSING_DATA_CONNECTION: + text = "Closing data connection."; + break; + case RESPONSE_230_USER_LOGGED_IN: + text = "User logged in, proceed."; + break; + case RESPONSE_331_PASSWORD_REQUIRED: + text = "Password required."; + break; + case RESPONSE_500_COMMAND_UNRECOGNIZED: + text = "Syntax error, command unrecognized."; + break; + case RESPONSE_502_COMMAND_NOT_IMPLEMENTED: + text = "Command not implemented."; + break; + case RESPONSE_503_BAD_SEQUENCE: + text = "Bad sequence of commands."; + break; + case RESPONSE_530_NOT_LOGGED_IN: + text = "Not logged in."; + break; + case RESPONSE_550_ACTION_NOT_TAKEN: + text = "Requested action not taken."; + break; + default: + break; + } + sendResponse(code, text); // Send the code AND the text to the FTP client. +} // FTPServer#sendResponse + + +/** + * Set the callbacks that are to be invoked to perform work. + * @param pCallbacks An instance of an FTPCallbacks based class. + */ +void FTPServer::setCallbacks(FTPCallbacks* pCallbacks) { + m_callbacks = pCallbacks; +} // FTPServer#setCallbacks + + +void FTPServer::setCredentials(std::string userid, std::string password) { + ESP_LOGD(LOG_TAG, ">> setCredentials: userid=%s", userid.c_str()); + m_loginRequired = true; + m_userid = userid; + m_password = password; + ESP_LOGD(LOG_TAG, "<< setCredentials"); +} // FTPServer#setCredentials + + +/** + * Set the TCP port we should listen on for FTP client requests. + */ +void FTPServer::setPort(uint16_t port) { + m_port = port; +} // FTPServer#setPort + + +/** + * Start being an FTP Server. + */ +void FTPServer::start() { + m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_serverSocket == -1) { + ESP_LOGD(LOG_TAG, "socket: %s", strerror(errno)); + } + + int enable = 1; + setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_port); + int rc = bind(m_serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "bind: %s", strerror(errno)); + } + rc = listen(m_serverSocket, 5); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "listen: %s", strerror(errno)); + } + while(1) { + waitForFTPClient(); + processCommand(); + } +} // FTPServer#start + + +/** + * Wait for a new client to connect. + */ +int FTPServer::waitForFTPClient() { + ESP_LOGD(LOG_TAG, ">> FTPServer::waitForFTPClient"); + + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_clientSocket = accept(m_serverSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + + char ipAddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddr, sizeof(ipAddr)); + ESP_LOGD(LOG_TAG, "Received connection from %s [%d]", ipAddr, clientAddress.sin_port); + + struct sockaddr_in socketAddressInfo; + unsigned int socketAddressInfoSize = sizeof(socketAddressInfo); + getsockname(m_clientSocket, (struct sockaddr*)&socketAddressInfo, &socketAddressInfoSize); + + inet_ntop(AF_INET, &socketAddressInfo.sin_addr, ipAddr, sizeof(ipAddr)); + ESP_LOGD(LOG_TAG, "Connected at %s [%d]", ipAddr, socketAddressInfo.sin_port); + ESP_LOGD(LOG_TAG, "<< FTPServer::waitForFTPClient: fd=%d\n", m_clientSocket); + + return m_clientSocket; +} // FTPServer::waitForFTPClient + diff --git a/cpp_utils/FTPServer.h b/cpp_utils/FTPServer.h new file mode 100644 index 00000000..7dc56f13 --- /dev/null +++ b/cpp_utils/FTPServer.h @@ -0,0 +1,137 @@ +/* + * FTPServer.h + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#ifndef NETWORKING_FTPSERVER_FTPSERVER_H_ +#define NETWORKING_FTPSERVER_FTPSERVER_H_ +#include +#include +#include +#include +#include + + +class FTPCallbacks { +public: + virtual void onStoreStart(std::string fileName); + virtual size_t onStoreData(uint8_t* data, size_t size); + virtual void onStoreEnd(); + virtual void onRetrieveStart(std::string fileName); + virtual size_t onRetrieveData(uint8_t *data, size_t size); + virtual void onRetrieveEnd(); + virtual std::string onDir(); + virtual ~FTPCallbacks(); +}; + +/** + * An implementation of FTPCallbacks that uses Posix File I/O to perform file access. + */ +class FTPFileCallbacks : public FTPCallbacks { +private: + std::ofstream m_storeFile; // File used to store data from the client. + std::ifstream m_retrieveFile; // File used to retrieve data for the client. + uint32_t m_byteCount; // Count of bytes sent over wire. +public: + void onStoreStart(std::string fileName) override; // Called for a STOR request. + size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. + void onStoreEnd() override; // Called at the end of a STOR request. + void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. + size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. + void onRetrieveEnd() override; // Called when we have retrieved all the data. + std::string onDir() override; // Called to retrieve all the directory entries. +}; + + +class FTPServer { +private: + int m_serverSocket; // The socket the FTP server is listening on. + int m_clientSocket; // The current client socket. + int m_dataSocket; // The data socket. + int m_passiveSocket; // The socket on which the server is listening for passive FTP connections. + uint16_t m_port; // The port the FTP server will use. + uint16_t m_dataPort; // The port for data connections. + uint32_t m_dataIp; // The ip address for data connections. + bool m_isPassive; // Are we in passive mode? If not, then we are in active mode. + bool m_isImage; // Are we in image mode? + size_t m_chunkSize; // The maximum chunk size. + std::string m_userid; // The required userid. + std::string m_password; // The required password. + std::string m_suppliedUserid; // The userid supplied from the USER command. + bool m_loginRequired; // Do we required a login? + bool m_isAuthenticated; // Have we authenticated? + std::string m_lastCommand; // The last command that was processed. + + FTPCallbacks* m_callbacks; // The callbacks for processing. + + void closeConnection(); + void closeData(); + void closePassive(); + void onAuth(std::istringstream& ss); + void onCwd(std::istringstream& ss); + void onList(std::istringstream& ss); + void onMkd(std::istringstream& ss); + void onNoop(std::istringstream& ss); + void onPass(std::istringstream& ss); + void onPasv(std::istringstream& ss); + void onPort(std::istringstream& ss); + void onPWD(std::istringstream& ss); + void onQuit(std::istringstream& ss); + void onRetr(std::istringstream& ss); + void onRmd(std::istringstream& ss); + void onStor(std::istringstream& ss); + void onSyst(std::istringstream& ss); + void onType(std::istringstream& ss); + void onUser(std::istringstream& ss); + void onXmkd(std::istringstream& ss); + void onXrmd(std::istringstream& ss); + + bool openData(); + + void receiveFile(std::string fileName); + void sendResponse(int code); + void sendResponse(int code, std::string text); + void sendData(uint8_t* pData, uint32_t size); + std::string listenPassive(); + int waitForFTPClient(); + void processCommand(); + +public: + FTPServer(); + virtual ~FTPServer(); + void setCredentials(std::string userid, std::string password); + void start(); + void setPort(uint16_t port); + void setCallbacks(FTPCallbacks* pFTPCallbacks); + static std::string getCurrentDirectory(); + class FileException: public std::exception { + + }; + + // Response codes. + static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; + static const int RESPONSE_200_COMMAND_OK = 200; + static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; + static const int RESPONSE_212_DIRECTORY_STATUS = 212; + static const int RESPONSE_213_FILE_STATUS = 213; + static const int RESPONSE_214_HELP_MESSAGE = 214; + static const int RESPONSE_220_SERVICE_READY = 220; + static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; + static const int RESPONSE_230_USER_LOGGED_IN = 230; + static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; + static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; + static const int RESPONSE_331_PASSWORD_REQUIRED = 331; + static const int RESPONSE_332_NEED_ACCOUNT = 332; + static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; + static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; + static const int RESPONSE_503_BAD_SEQUENCE = 503; + static const int RESPONSE_530_NOT_LOGGED_IN = 530; + static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; + static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; +}; + + + +#endif /* NETWORKING_FTPSERVER_FTPSERVER_H_ */ diff --git a/cpp_utils/GPIO.cpp b/cpp_utils/GPIO.cpp index 4188d307..23cf6fb7 100644 --- a/cpp_utils/GPIO.cpp +++ b/cpp_utils/GPIO.cpp @@ -9,6 +9,7 @@ #include #include "sdkconfig.h" #include +#include #include "GeneralUtils.h" static const char* LOG_TAG = "GPIO"; From 631b53cc790816565a1def3f76962594a0cdb7ea Mon Sep 17 00:00:00 2001 From: FedericoBusero <35894905+FedericoBusero@users.noreply.github.com> Date: Thu, 17 May 2018 22:45:59 +0200 Subject: [PATCH 211/310] Add Service UUID advertising (iOS support) Lots of apps (e.g. Blynk, RemoteXY) do not find the device during scanning on iOS, whereas other devices with the same service UUID are found. The origin of the problem is that in the advertisment, the service UUID is missing. This extra line fixes this problem. --- cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino index 35b570b9..ec014db7 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino @@ -97,6 +97,7 @@ void setup() { pService->start(); // Start advertising + pServer->getAdvertising()->addServiceUUID(pService->getUUID()); pServer->getAdvertising()->start(); Serial.println("Waiting a client connection to notify..."); } From 984a7aba4253fb29e25f2022e909f17adc447d22 Mon Sep 17 00:00:00 2001 From: olehs Date: Sun, 20 May 2018 15:59:04 +0300 Subject: [PATCH 212/310] reset m_haveServices in clearServices() #522 --- cpp_utils/BLEClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 57ff4d21..f4524980 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -77,6 +77,7 @@ void BLEClient::clearServices() { delete myPair.second; } m_servicesMap.clear(); + m_haveServices = false; ESP_LOGD(LOG_TAG, "<< clearServices"); } // clearServices From 70dbad12b9ca48b9763c851bf48cb4e337bc8355 Mon Sep 17 00:00:00 2001 From: Oleg Date: Sun, 20 May 2018 21:10:13 +0300 Subject: [PATCH 213/310] Call give() to all m_semaphore***CmplEvt on disconnect This allows to avoid infinit lock in situations, where DISCONNECT occurs during long calls --- cpp_utils/BLEClient.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index f4524980..141cf0f5 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -167,6 +167,8 @@ void BLEClient::gattClientEventHandler( m_pClientCallbacks->onDisconnect(this); } m_isConnected = false; + m_semaphoreRssiCmplEvt.give(); + m_semaphoreSearchCmplEvt.give(1); break; } // ESP_GATTC_DISCONNECT_EVT @@ -214,7 +216,7 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // case ESP_GATTC_SEARCH_CMPL_EVT: { - m_semaphoreSearchCmplEvt.give(); + m_semaphoreSearchCmplEvt.give(0); break; } // ESP_GATTC_SEARCH_CMPL_EVT @@ -367,8 +369,8 @@ std::map* BLEClient::getServices() { ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return &m_servicesMap; } - m_semaphoreSearchCmplEvt.wait("getServices"); - m_haveServices = true; // Remember that we now have services. + // If sucessfull, remember that we now have services. + m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0); ESP_LOGD(LOG_TAG, "<< getServices"); return &m_servicesMap; } // getServices From 0d6744244c5701d9d3acdfdfc0a5bf0441ae0b86 Mon Sep 17 00:00:00 2001 From: chegewara Date: Fri, 25 May 2018 23:54:50 +0200 Subject: [PATCH 214/310] multiple services with the same uuid --- cpp_utils/BLEServer.cpp | 9 +++++---- cpp_utils/BLEServer.h | 5 ++++- cpp_utils/BLEService.cpp | 5 +++-- cpp_utils/BLEService.h | 1 + cpp_utils/BLEServiceMap.cpp | 36 ++++++++++++++++++++++++++++++++---- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index c33afdf2..ad323477 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -75,15 +75,16 @@ BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); m_semaphoreCreateEvt.take("createService"); + BLEService* pService = new BLEService(uuid, numHandles); // Check that a service with the supplied UUID does not already exist. if (m_serviceMap.getByUUID(uuid) != nullptr) { - ESP_LOGE(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); - m_semaphoreCreateEvt.give(); - return nullptr; + //m_semaphoreCreateEvt.give(); + //return nullptr; + pService->m_id = 1; } - BLEService* pService = new BLEService(uuid, numHandles); m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index bc0ef05d..e21deeef 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -40,10 +40,13 @@ class BLEServiceMap { void setByUUID(const char* uuid, BLEService* service); void setByUUID(BLEUUID uuid, BLEService* service); std::string toString(); + BLEService* getFirst(); + BLEService* getNext(); private: - std::map m_uuidMap; std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; }; diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 4e59c4a4..4a427fbb 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -73,7 +73,7 @@ void BLEService::executeCreate(BLEServer *pServer) { esp_gatt_srvc_id_t srvc_id; srvc_id.is_primary = true; - srvc_id.id.inst_id = 0; + srvc_id.id.inst_id = m_id; srvc_id.id.uuid = *m_uuid.getNative(); esp_err_t errRc = ::esp_ble_gatts_create_service( getServer()->getGattsIf(), @@ -292,7 +292,8 @@ void BLEService::handleGATTServerEvent( // * - bool is_primary // case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid))) { + ESP_LOGE(LOG_TAG, "%d", param->create.service_handle); + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { setHandle(param->create.service_handle); m_semaphoreCreateEvt.give(); } diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 2b78e620..79958f3d 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -64,6 +64,7 @@ class BLEService { void start(); std::string toString(); uint16_t getHandle(); + uint8_t m_id = 0; private: BLEService(const char* uuid, uint32_t numHandles); diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index 8fdbd5ac..96811ad2 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -27,8 +27,8 @@ BLEService* BLEServiceMap::getByUUID(const char* uuid) { */ BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { for (auto &myPair : m_uuidMap) { - if (myPair.second->getUUID().equals(uuid)) { - return myPair.second; + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; } } //return m_uuidMap.at(uuid.toString()); @@ -54,7 +54,7 @@ BLEService* BLEServiceMap::getByHandle(uint16_t handle) { */ void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService *service) { - m_uuidMap.insert(std::pair(uuid.toString(), service)); + m_uuidMap.insert(std::pair(service, uuid.toString())); } // setByUUID @@ -89,7 +89,35 @@ void BLEServiceMap::handleGATTServerEvent( esp_ble_gatts_cb_param_t *param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); + myPair.first->handleGATTServerEvent(event, gatts_if, param); } } + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +BLEService* BLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +BLEService* BLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + #endif /* CONFIG_BT_ENABLED */ From eb836aaff47607b32da1d6de07942568a9748ee9 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 26 May 2018 02:59:22 +0200 Subject: [PATCH 215/310] multiple services with the same uuid --- cpp_utils/BLEServer.cpp | 7 ++++--- cpp_utils/BLEServer.h | 4 ++-- cpp_utils/BLEService.cpp | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index ad323477..bc079813 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -69,22 +69,23 @@ BLEService* BLEServer::createService(const char* uuid) { * of a new service. Every service must have a unique UUID. * @param [in] uuid The UUID of the new service. * @param [in] numHandles The maximum number of handles associated with this service. + * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. * @return A reference to the new service object. */ -BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); m_semaphoreCreateEvt.take("createService"); - BLEService* pService = new BLEService(uuid, numHandles); // Check that a service with the supplied UUID does not already exist. if (m_serviceMap.getByUUID(uuid) != nullptr) { ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); //m_semaphoreCreateEvt.give(); //return nullptr; - pService->m_id = 1; } + BLEService* pService = new BLEService(uuid, numHandles); + pService->m_id = inst_id; m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index e21deeef..d53510d6 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -57,10 +57,11 @@ class BLEServer { public: uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); + uint16_t getGattsIf(); private: @@ -81,7 +82,6 @@ class BLEServer { void createApp(uint16_t appId); uint16_t getConnId(); - uint16_t getGattsIf(); void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void registerApp(); diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 4a427fbb..6636ca5c 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -292,7 +292,6 @@ void BLEService::handleGATTServerEvent( // * - bool is_primary // case ESP_GATTS_CREATE_EVT: { - ESP_LOGE(LOG_TAG, "%d", param->create.service_handle); if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { setHandle(param->create.service_handle); m_semaphoreCreateEvt.give(); From b059dad94f861620dc6a7cda1bb1f9ed331a61e6 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 26 May 2018 03:03:16 +0200 Subject: [PATCH 216/310] multiple services with the same uuid --- cpp_utils/BLEServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index d53510d6..6c71679a 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -61,7 +61,6 @@ class BLEServer { BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); - uint16_t getGattsIf(); private: @@ -82,6 +81,7 @@ class BLEServer { void createApp(uint16_t appId); uint16_t getConnId(); + uint16_t getGattsIf(); void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void registerApp(); From db1d45e19fcf0e3e6c32bd1fa232fb79aeebbde2 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 26 May 2018 15:22:14 -0500 Subject: [PATCH 217/310] Addition of console samples. --- console/main.cpp | 14 + console/test_argtable.cpp | 69 +++ console/test_console.cpp | 95 +++ console/test_linenoise.cpp | 40 ++ eclipse/c_includes.xml | 1 + networking/FTPServer/FTPCallbacks.cpp | 154 +++++ networking/FTPServer/FTPServer.cpp | 819 ++++++++++++++++++++++++++ networking/FTPServer/FTPServer.h | 137 +++++ networking/FTPServer/README.md | 76 +++ networking/FTPServer/main.cpp | 12 + 10 files changed, 1417 insertions(+) create mode 100644 console/main.cpp create mode 100644 console/test_argtable.cpp create mode 100644 console/test_console.cpp create mode 100644 console/test_linenoise.cpp create mode 100644 networking/FTPServer/FTPCallbacks.cpp create mode 100644 networking/FTPServer/FTPServer.cpp create mode 100644 networking/FTPServer/FTPServer.h create mode 100644 networking/FTPServer/README.md create mode 100644 networking/FTPServer/main.cpp diff --git a/console/main.cpp b/console/main.cpp new file mode 100644 index 00000000..f5715f5d --- /dev/null +++ b/console/main.cpp @@ -0,0 +1,14 @@ +extern "C" { + void app_main(void); +} + +extern void test_linenoise(); +extern void test_argtable(); +extern void test_console(); + +void app_main(void) +{ + //test_linenoise(); + //test_argtable(); + test_console(); +} diff --git a/console/test_argtable.cpp b/console/test_argtable.cpp new file mode 100644 index 00000000..160b728c --- /dev/null +++ b/console/test_argtable.cpp @@ -0,0 +1,69 @@ +/** + * Test of argtable + * + * @author Neil Kolban + * @date 2018-05-26 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" + +void test_argtable(void) +{ + // Boiler plate setup for using linenoise + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0); + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + + linenoiseClearScreen(); + linenoiseSetMultiLine(0); + + struct arg_lit* help = arg_lit0("h", "help", "Generate some help!"); + struct arg_lit* test = arg_lit1("t", "test", "Generate some help!"); + struct arg_end* end = arg_end(20); + + void *argtable[] = { + help, + test, + end + }; + + char *argv[10]; + + while(1) { + char* line = linenoise("Enter command > "); + if (line != NULL) { + printf("Got: %s\n", line); + size_t argc = esp_console_split_argv(line, argv, 10); + printf("Parsed to %d argc count\n", argc); + for (int i=0; i0) { + printf("Number of errors: %d\n", numErrors); + arg_print_errors(stdout, end, "myprog"); + arg_print_syntaxv(stdout, argtable, "Here:"); + } + else { + if (help->count > 0) { + printf("Found help!\n"); + } + } + linenoiseHistoryAdd(line); + linenoiseFree(line); + } // Line is not null + printf("--------------\n"); + } // End while loop +} diff --git a/console/test_console.cpp b/console/test_console.cpp new file mode 100644 index 00000000..20c240ad --- /dev/null +++ b/console/test_console.cpp @@ -0,0 +1,95 @@ +/** + * Test of console + * + * @author Neil Kolban + * @date 2018-05-26 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" + + +struct arg_lit* show_arg_show = arg_lit0("s", "show", "generate show"); +struct arg_end* show_arg_end = arg_end(10); +void *show_argtable[] = { + show_arg_show, + show_arg_end +}; + +int runShow(int argc, char *argv[]) { + printf("Found show!\n"); + return 0; +} + +struct arg_str* greet_arg_name = arg_str1("n", "name", "", "generate help"); +struct arg_end* greet_arg_end = arg_end(10); +void *greet_argtable[] = { + greet_arg_name, + greet_arg_end +}; + +int runGreet(int argc, char *argv[]) { + printf("Found Greet!\n"); + int numErrors = arg_parse(argc, argv, greet_argtable); + if (numErrors > 0) { + arg_print_errors(stdout, greet_arg_end, "greet"); + } else { + printf("Hello %s\n", greet_arg_name->sval[0]); + } + return 0; +} + +void test_console(void) +{ + // Boiler plate setup for using linenoise + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0); + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + esp_console_config_t consoleConfig; + consoleConfig.max_cmdline_args = 5; + consoleConfig.max_cmdline_length = 100; + + esp_console_init(&consoleConfig); + esp_console_register_help_command(); + + esp_console_cmd_t consoleCmd; + consoleCmd.command = "show"; + consoleCmd.func = runShow; + consoleCmd.help = "Show something"; + consoleCmd.argtable = show_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "greet"; + consoleCmd.func = runGreet; + consoleCmd.help = "Greet someone"; + consoleCmd.argtable = greet_argtable; + esp_console_cmd_register(&consoleCmd); + + + linenoiseClearScreen(); + linenoiseSetMultiLine(0); + + + while(1) { + char* line = linenoise("Enter command > "); + if (line != NULL) { + printf("Got: %s\n", line); + int ret; + esp_console_run(line, &ret); + linenoiseHistoryAdd(line); + linenoiseFree(line); + } // Line is not null + printf("--------------\n"); + } // End while loop +} diff --git a/console/test_linenoise.cpp b/console/test_linenoise.cpp new file mode 100644 index 00000000..b2d36cf4 --- /dev/null +++ b/console/test_linenoise.cpp @@ -0,0 +1,40 @@ +/** + * Test of linenoise + * + * @author Neil Kolban + * @date 2018-05-26 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" + +void test_linenoise(void) +{ + // Boiler plate setup for using linenoise + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0); + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + + linenoiseClearScreen(); + linenoiseSetMultiLine(0); + + while(1) { + char* line = linenoise("Enter command > "); + if (line != NULL) { + printf("Got: %s\n", line); + linenoiseHistoryAdd(line); + linenoiseFree(line); + } // Line is not null + printf("--------------\n"); + } // End while loop +} diff --git a/eclipse/c_includes.xml b/eclipse/c_includes.xml index ee8feb85..eaa53ba2 100644 --- a/eclipse/c_includes.xml +++ b/eclipse/c_includes.xml @@ -74,6 +74,7 @@ ${IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include ${IDF_PATH}/components/fatfs/src ${IDF_PATH}/components/wear_levelling/include +${IDF_PATH}/components/console diff --git a/networking/FTPServer/FTPCallbacks.cpp b/networking/FTPServer/FTPCallbacks.cpp new file mode 100644 index 00000000..99a6e111 --- /dev/null +++ b/networking/FTPServer/FTPCallbacks.cpp @@ -0,0 +1,154 @@ +#include "FTPServer.h" +#include +#include +#include + + +/** + * Called at the start of a STOR request. The file name is the name of the file the client would like to + * save. + */ +void FTPFileCallbacks::onStoreStart(std::string fileName) { + printf(">> FTPFileCallbacks::onStoreStart: fileName=%s\n", fileName.c_str()); + m_storeFile.open(fileName, std::ios::binary); // Open the file for writing. + if (m_storeFile.fail()) { + throw FTPServer::FileException(); + } + printf("<< FTPFileCallbacks::onStoreStart\n"); +} // FTPFileCallbacks#onStoreStart + + +/** + * Called when the client presents a new chunk of data to be saved. + */ +size_t FTPFileCallbacks::onStoreData(uint8_t* data, size_t size) { + printf(">> FTPFileCallbacks::onStoreData: size=%ld\n", size); + m_storeFile.write((char *)data, size); // Store data received. + printf("<< FTPFileCallbacks::onStoreData: size=%ld\n", size); + return size; +} // FTPFileCallbacks#onStoreData + + +/** + * Called at the end of a STOR request. This indicates that the client has completed its transmission of the + * file. + */ +void FTPFileCallbacks::onStoreEnd() { + printf(">> FTPFileCallbacks::onStoreEnd\n"); + m_storeFile.close(); // Close the open file. + printf("<< FTPFileCallbacks::onStoreEnd\n"); +} // FTPFileCallbacks#onStoreEnd + + +/** + * Called when the client requests retrieval of a file. + */ +void FTPFileCallbacks::onRetrieveStart(std::string fileName) { + printf(">> FTPFileCallbacks::onRetrieveStart: fileName=%s\n", fileName.c_str()); + m_byteCount = 0; + m_retrieveFile.open(fileName, std::ios::binary); + if (m_retrieveFile.fail()) { + printf("<< FTPFileCallbacks::onRetrieveStart: ***FileException***\n"); + throw FTPServer::FileException(); + } + printf("<< FTPFileCallbacks::onRetrieveStart\n"); +} // FTPFileCallbacks#onRetrieveStart + + +/** + * Called when the client is ready to receive the next piece of the file. To indicate that there + * is no more data to send, return a size of 0. + * @param data The data buffer that we can fill to return data back to the client. + * @param size The maximum size of the data buffer that we can populate. + * @return The size of data being returned. Return 0 to indicate that there is no more data to return. + */ +size_t FTPFileCallbacks::onRetrieveData(uint8_t* data, size_t size) { + printf(">> FTPFileCallbacks::onRetrieveData\n"); + m_retrieveFile.read((char *)data, size); + size_t readSize = m_retrieveFile.gcount(); + m_byteCount += readSize; + printf("<< FTPFileCallbacks::onRetrieveData: sizeRead=%ld\n", readSize); + return m_retrieveFile.gcount(); // Return the number of bytes read. +} // FTPFileCallbacks#onRetrieveData + + +/** + * Called when the retrieval has been completed. + */ +void FTPFileCallbacks::onRetrieveEnd() { + printf(">> FTPFileCallbacks::onRetrieveEnd\n"); + m_retrieveFile.close(); + printf("<< FTPFileCallbacks::onRetrieveEnd: bytesTransmitted=%d\n", m_byteCount); +} // FTPFileCallbacks#onRetrieveEnd + + +/** + * Return a list of files in the file system. + * @return a list of files in the file system. + */ +std::string FTPFileCallbacks::onDir() { + + DIR* dir = opendir(FTPServer::getCurrentDirectory().c_str()); + std::stringstream ss; + while(1) { + struct dirent* pDirentry = readdir(dir); + if (pDirentry == nullptr) { + break; + } + ss << pDirentry->d_name << "\r\n"; + } + closedir(dir); + return ss.str(); +} // FTPFileCallbacks#onDir + + +/// ---- END OF FTPFileCallbacks + + +void FTPCallbacks::onStoreStart(std::string fileName) { + printf(">> FTPCallbacks::onStoreStart: fileName=%s\n", fileName.c_str()); + printf("<< FTPCallbacks::onStoreStart\n"); +} // FTPCallbacks#onStoreStart + + +size_t FTPCallbacks::onStoreData(uint8_t* data, size_t size) { + printf(">> FTPCallbacks::onStoreData: size=%ld\n", size); + printf("<< FTPCallbacks::onStoreData\n"); + return 0; +} // FTPCallbacks#onStoreData + + +void FTPCallbacks::onStoreEnd() { + printf(">> FTPCallbacks::onStoreEnd\n"); + printf("<< FTPCallbacks::onStoreEnd\n"); +} // FTPCallbacks#onStoreEnd + + +void FTPCallbacks::onRetrieveStart(std::string fileName) { + printf(">> FTPCallbacks::onRetrieveStart\n"); + printf("<< FTPCallbacks::onRetrieveStart\n"); +} // FTPCallbacks#onRetrieveStart + + +size_t FTPCallbacks::onRetrieveData(uint8_t *data, size_t size) { + printf(">> FTPCallbacks::onRetrieveData\n"); + printf("<< FTPCallbacks::onRetrieveData: 0\n"); + return 0; +} // FTPCallbacks#onRetrieveData + + +void FTPCallbacks::onRetrieveEnd() { +printf(">> FTPCallbacks::onRetrieveEnd\n"); +printf("<< FTPCallbacks::onRetrieveEnd\n"); +} // FTPCallbacks#onRetrieveEnd + + +std::string FTPCallbacks::onDir() { + printf(">> FTPCallbacks::onDir\n"); + printf("<< FTPCallbacks::onDir\n"); + return ""; +} // FTPCallbacks#onDir + +FTPCallbacks::~FTPCallbacks() { + +} // FTPCallbacks#~FTPCallbacks diff --git a/networking/FTPServer/FTPServer.cpp b/networking/FTPServer/FTPServer.cpp new file mode 100644 index 00000000..87899c3b --- /dev/null +++ b/networking/FTPServer/FTPServer.cpp @@ -0,0 +1,819 @@ +/* + * FTPServer.cpp + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#include "FTPServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// trim from start (in place) +static void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} // ltrim + + +// trim from end (in place) +static void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} // rtrim + + +// trim from both ends (in place) +static void trim(std::string &s) { + ltrim(s); + rtrim(s); +} // trim + + +FTPServer::FTPServer() { + printf(">> FTPServer()\n"); + + m_dataSocket = -1; + m_clientSocket = -1; + m_dataPort = -1; + m_dataIp = -1; + m_passiveSocket = -1; + m_serverSocket = -1; + + m_callbacks = nullptr; + m_isPassive = false; + m_isImage = true; + m_chunkSize = 4096; + m_port = 21; // The default Server-PI port + m_loginRequired = false; + m_isAuthenticated = false; + m_userid = ""; + m_password = ""; + + printf("<< FTPServer()\n"); +} // FTPServer#FTPServer + + +FTPServer::~FTPServer() { + // Nothing to do here by default. +} // FTPServer#~FTPServer + + +/** + * Close the connection to the FTP client. + */ +void FTPServer::closeConnection() { + printf(">> closeConnection\n"); + close(m_clientSocket); + printf("<< closeConnection\n"); +} // FTPServer#closeConnection + + +/** + * Close a previously opened data connection. + */ +void FTPServer::closeData() { + printf(">> closeData\n"); + close(m_dataSocket); + m_dataSocket = -1; + printf("<< closeData\n"); +} // FTPServer#closeData + + +/** + * Close the passive listening socket that was opened by listenPassive. + */ +void FTPServer::closePassive() { + printf(">> closePassive\n"); + close(m_passiveSocket); + m_passiveSocket = -1; + printf("<< closePassive\n"); +} // FTPServer#closePassive + + +/** + * Retrieve the current directory. + */ +/* STATIC */ std::string FTPServer::getCurrentDirectory() { + char maxDirectory[256]; + std::string currentDirectory = getcwd(maxDirectory, sizeof(maxDirectory)); + return currentDirectory; +} // FTPServer#getCurrentDirectory + + +/** + * Create a listening socket for the new passive connection. + * @return a String for the passive parameters. + */ +std::string FTPServer::listenPassive() { + printf(">> listenPassive\n"); + + m_passiveSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_passiveSocket == -1) { + printf("socket: %s", strerror(errno)); + } + + struct sockaddr_in clientAddrInfo; + unsigned int addrInfoSize = sizeof(clientAddrInfo); + getsockname(m_clientSocket, (struct sockaddr*)&clientAddrInfo, &addrInfoSize); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(0); + int rc = bind(m_passiveSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + printf("bind: %s\n", strerror(errno)); + } + + rc = listen(m_passiveSocket, 5); + if (rc == -1) { + printf("listen: %s", strerror(errno)); + } + + unsigned int addrLen = sizeof(serverAddress); + rc = getsockname(m_passiveSocket, (struct sockaddr*)&serverAddress, &addrLen); + if (rc == -1) { + printf("getsockname: %s\n", strerror(errno)); + } + + + std::stringstream ss; + ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 16) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 24) & 0xff) << + "," << ((serverAddress.sin_port >> 0) & 0xff) << + "," << ((serverAddress.sin_port >> 8) & 0xff); + std::string retStr = ss.str(); + + printf("<< listenPassive: %s\n", retStr.c_str()); + return retStr; +} // FTPServer#listenPassive + + +/** + * Handle the AUTH command. + */ +void FTPServer::onAuth(std::istringstream& ss) { + std::string param; + ss >> param; + printf(">> onAuth: %s\n", param.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + printf("<< onAuth\n"); +} // FTPServer#onAuth + + +/** + * Change the current working directory. + * @param ss A string stream where the first parameter is the directory to change to. + */ +void FTPServer::onCwd(std::istringstream& ss) { + std::string path; + ss >> path; + printf(">> onCwd: path=%s\n", path.c_str()); + chdir(path.c_str()); + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); + printf("<< onCwd\n"); +} // FTPServer#onCwd + + +/** + * Process the client transmitted LIST request. + */ +void FTPServer::onList(std::istringstream& ss) { + std::string directory; + ss >> directory; + printf(">> onList: directory=%s\n", directory.c_str()); + + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + if (m_callbacks != nullptr) { + std::string dirString = m_callbacks->onDir(); + sendData((uint8_t *)dirString.data(), dirString.length()); + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + printf("<< onList\n"); +} // FTPServer#onList + + +void FTPServer::onMkd(std::istringstream &ss) { + std::string path; + ss >> path; + printf(">> onMkd: path=%s\n", path.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onMkd\n"); +} // FTPServer#onMkd + + +/** + * Process a NOOP operation. + */ +void FTPServer::onNoop(std::istringstream& ss) { + printf(">> onNoop\n"); + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + printf("<< onNoop\n"); +} // FTPServer#onNoop + + +/** + * Process PORT request. The information provided is encoded in the parameter as + * h1,h2,h3,h4,p1,p2 where h1,h2,h3,h4 is the IP address we should connect to + * and p1,p2 is the port number. The data is MSB + * + * Our logic does not form any connection but remembers the ip address and port number + * to be used for a subsequence data connection. + * + * Possible responses: + * 200 + * 500, 501, 421, 530 + */ +void FTPServer::onPort(std::istringstream& ss) { + printf(">> onPort\n"); + char c; + uint16_t h1, h2, h3, h4, p1, p2; + ss >> h1 >> c >> h2 >> c >> h3 >> c >> h4 >> c >> p1 >> c >> p2; + m_dataPort = p1*256 + p2; + printf("%d.%d.%d.%d %d\n", h1, h2, h3, h4, m_dataPort); + m_dataIp = h1<<24 | h2<<16 | h3<<8 | h4; + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + m_isPassive = false; + + printf("<< onPort\n"); +} // FTPServer#onPort + + +/** + * Process the PASS command. + * Possible responses: + * 230 + * 202 + * 530 + * 500, 501, 503, 421 + * 332 + */ +void FTPServer::onPass(std::istringstream& ss) { + std::string password; + ss >> password; + printf(">> onPass: password=%s\n", password.c_str()); + + // If the immediate last command wasn't USER then don't try and process PASS. + if (m_lastCommand != "USER") { + sendResponse(RESPONSE_503_BAD_SEQUENCE); + printf("<< onPass\n"); + return; + } + + // Compare the supplied userid and passwords. + if (m_userid == m_suppliedUserid && password == m_password) { + sendResponse(RESPONSE_230_USER_LOGGED_IN); + m_isAuthenticated = true; + } else { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + closeConnection(); + m_isAuthenticated = false; + } + + + printf("<< onPass\n"); +} // FTPServer#onPass + + +/** + * Process the PASV command. + * Possible responses: + * 227 + * 500, 501, 502, 421, 530 + */ +void FTPServer::onPasv(std::istringstream& ss) { + printf(">> onPasv\n"); + std::string ipInfo = listenPassive(); + std::ostringstream responseTextSS; + responseTextSS << "Entering Passive Mode (" << ipInfo << ")."; + std::string responseText; + responseText = responseTextSS.str(); + sendResponse(RESPONSE_227_ENTERING_PASSIVE_MODE, responseText.c_str()); + m_isPassive = true; + + printf("<< onPasv\n"); +} // FTPServer#onPasv + + +/** + * Process the PWD command to determine our current working directory. + * Possible responses: + * 257 + * 500, 501, 502, 421, 550 + */ +void FTPServer::onPWD(std::istringstream& ss) { + printf(">> onPWD\n"); + sendResponse(257, "\"" + getCurrentDirectory() + "\""); + printf("<< onPWD: %s\n", getCurrentDirectory().c_str()); +} // FTPServer#onPWD + + +/** + * Possible responses: + * 221 + * 500 + */ +void FTPServer::onQuit(std::istringstream& ss) { + printf(">> onQuit\n"); + sendResponse(FTPServer::RESPONSE_221_CLOSING_CONTROL_CONNECTION); // Service closing control connection. + closeConnection(); // Close the connection to the client. + printf("<< onQuit\n"); +} // FTPServer#onQuit + + +/** + * Process a RETR command. The client sends this command to retrieve the content of a file. + * The name of the file is the first parameter in the input stream. + * + * Possible responses: + * 125, 150 + * (110) + * 226, 250 + * 425, 426, 451 + * 450, 550 + * 500, 501, 421, 530 + * @param ss The parameter stream. + */ +void FTPServer::onRetr(std::istringstream& ss) { + + // We open a data connection back to the client. We then invoke the callback to indicate that we have + // started a retrieve operation. We call the retrieve callback to request the next chunk of data and + // transmit this down the data connection. We repeat this until there is no more data to send at which + // point we close the data connection and we are done. + printf(">> onRetr\n"); + std::string fileName; + + ss >> fileName; + uint8_t data[m_chunkSize]; + + + if (m_callbacks != nullptr) { + try { + m_callbacks->onRetrieveStart(fileName); + } catch(FTPServer::FileException& e) { + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + printf("<< onRetr: Returned 550 to client.\n"); + return; + } + } + + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + openData(); + if (m_callbacks != nullptr) { + int readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + while(readSize > 0) { + sendData(data, readSize); + readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + } + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + printf("<< onRetr\n"); +} // FTPServer#onRetr + + +void FTPServer::onRmd(std::istringstream &ss) { + printf(">> onRmd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onRmd\n"); +} // FTPServer#onRmd + + +/** + * Called to process a STOR request. This means that the client wishes to store a file + * on the server. The name of the file is found in the parameter. + */ +void FTPServer::onStor(std::istringstream& ss) { + printf(">> onStor\n"); + std::string fileName; + ss >> fileName; + + receiveFile(fileName); + printf("<< onStor\n"); +} // FTPServer#onStor + + +void FTPServer::onSyst(std::istringstream& ss) { + printf(">> onSyst\n"); + sendResponse(215, "UNIX Type: L8"); + printf("<< onSyst\n"); +} // FTPServer#onSyst + + +/** + * Process a TYPE request. The parameter that follows is the type of transfer we wish + * to process. Types include: + * I and A. + * + * Possible responses: + * 200 + * 500, 501, 504, 421, 530 + */ +void FTPServer::onType(std::istringstream& ss) { + printf(">> onType\n"); + std::string type; + ss >> type; + if (type.compare("I") == 0) { + m_isImage = true; + } else { + m_isImage = false; + } + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + printf("<< onType: isImage=%d\n", m_isImage); +} // FTPServer#onType + + +/** + * Process a USER request. The parameter that follows is the identity of the user. + * + * Possible responses: + * 230 + * 530 + * 500, 501, 421 + * 331, 332 + * + */ +void FTPServer::onUser(std::istringstream& ss) { + // When we receive a user command, we next want to know if we should ask for a password. If the m_loginRequired + // flag is set then we do indeed want a password and will send the response that we wish one. + + std::string userName; + ss >> userName; + printf(">> onUser: userName=%s\n", userName.c_str()); + if (m_loginRequired) { + sendResponse(FTPServer::RESPONSE_331_PASSWORD_REQUIRED); + } else { + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + } + m_suppliedUserid = userName; // Save the username that was supplied. + printf("<< onUser\n"); +} // FTPServer#onUser + + +void FTPServer::onXmkd(std::istringstream &ss) { + printf(">> onXmkd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onXmkd\n"); +} // FTPServer#onXmkd + + +void FTPServer::onXrmd(std::istringstream &ss) { + printf(">> onXrmd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onXrmd\n"); +} // FTPServer#onXrmd + + +/** + * Open a data connection with the client. + * We will use closeData() to close the connection. + * @return True if the data connection succeeded. + */ +bool FTPServer::openData() { + if (m_isPassive) { + // Handle a passive connection ... here we receive a connection from the client from the passive socket. + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_dataSocket = accept(m_passiveSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + if (m_dataSocket == -1) { + printf("FTPServer::openData: accept(): %s\n", strerror(errno)); + closePassive(); + return false; + } + closePassive(); + } else { + // Handle an active connection ... here we connect to the client. + m_dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(m_dataIp); + serverAddress.sin_port = htons(m_dataPort); + + int rc = connect(m_dataSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + if (rc == -1) { + printf("FTPServer::openData: connect(): %s\n", strerror(errno)); + return false; + } + } + return true; +} // FTPServer#openData + + +/** + * Process commands received from the client. + */ +void FTPServer::processCommand() { + sendResponse(FTPServer::RESPONSE_220_SERVICE_READY); // Service ready. + printf(">> FTPServer::processCommand\n"); + m_lastCommand = ""; + while(1) { + std::string line = ""; + char currentChar; + char lastChar = '\0'; + int rc = recv(m_clientSocket, ¤tChar, 1, 0); + while(rc != -1 && rc!=0) { + line += currentChar; + if (lastChar == '\r' && currentChar == '\n') { + break; + } + printf("%c\n", currentChar); + lastChar = currentChar; + rc = recv(m_clientSocket, ¤tChar, 1, 0); + } // End while we are waiting for a line. + + if (rc == 0 || rc == -1) { // If we didn't get a line or an error, then we have finished processing commands. + break; + } + + std::string command; + std::istringstream ss(line); + getline(ss, command, ' '); + trim(command); + + // We now have a command to process. + + printf("Command: \"%s\"\n", command.c_str()); + if (command.compare("USER")==0) { + onUser(ss); + } + else if (command.compare("PASS")==0) { + onPass(ss); + } + else if (m_loginRequired && !m_isAuthenticated) { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + } + else if (command.compare("PASV")==0) { + onPasv(ss); + } + else if (command.compare("SYST")==0) { + onSyst(ss); + } + else if (command.compare("PORT")==0) { + onPort(ss); + } + else if (command.compare("LIST")==0) { + onList(ss); + } + else if (command.compare("TYPE")==0) { + onType(ss); + } + else if (command.compare("RETR")==0) { + onRetr(ss); + } + else if (command.compare("QUIT")==0) { + onQuit(ss); + } + else if (command.compare("AUTH")==0) { + onAuth(ss); + } + else if (command.compare("STOR")==0) { + onStor(ss); + } + else if (command.compare("PWD")==0) { + onPWD(ss); + } + else if (command.compare("MKD")==0) { + onMkd(ss); + } + else if (command.compare("XMKD")==0) { + onXmkd(ss); + } + else if (command.compare("RMD")==0) { + onRmd(ss); + } + else if (command.compare("XRMD")==0) { + onXrmd(ss); + } + else if (command.compare("CWD")==0) { + onCwd(ss); + } + else { + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + } + m_lastCommand = command; + } // End loop processing commands. + + close(m_clientSocket); // We won't be processing any further commands from this client. + printf("<< FTPServer::processCommand\n"); +} // FTPServer::processCommand + + +/** + * Receive a file from the FTP client (STOR). The name of the file to be created is passed as a + * parameter. + */ +void FTPServer::receiveFile(std::string fileName) { + printf(">> receiveFile: %s\n", fileName.c_str()); + if (m_callbacks != nullptr) { + try { + m_callbacks->onStoreStart(fileName); + } catch(FTPServer::FileException& e) { + printf("Caught a file exception!\n"); + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + return; + } + } + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + uint8_t buf[m_chunkSize]; + uint32_t totalSizeRead = 0; + while(1) { + int rc = recv(m_dataSocket, &buf, m_chunkSize, 0); + if (rc <= 0) { + break; + } + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveData(buf, rc); + } + totalSizeRead += rc; + } + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + closeData(); + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + printf("<< receiveFile: totalSizeRead=%d\n", totalSizeRead); +} // FTPServer#receiveFile + + +/** + * Send data to the client over the data connection previously opened with a call to openData(). + * @param pData A pointer to the data to send. + * @param size The number of bytes to send. + */ +void FTPServer::sendData(uint8_t* pData, uint32_t size) { + printf(">> FTPServer::sendData: size=%d\n", size); + int rc = send(m_dataSocket, pData, size, 0); + if (rc == -1) { + printf("FTPServer::sendData: send(): %s\n", strerror(errno)); + } + printf("<< FTPServer::sendData\n"); +} // FTPServer#sendData + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. + */ +void FTPServer::sendResponse(int code, std::string text) { + printf(">> sendResponse: (%d) %s\n", code, text.c_str()); + std::ostringstream ss; + ss << code << " " << text << "\r\n"; + int rc = send(m_clientSocket, ss.str().data(), ss.str().length(), 0); + printf("<< sendResponse\n"); +} // FTPServer#sendResponse + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. In this function, a standard piece of text is used based on + * the code. + */ +void FTPServer::sendResponse(int code) { + std::string text = "unknown"; + + switch(code) { // Map the code to a text string. + case RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION: + text = "File status okay; about to open data connection."; + break; + case RESPONSE_200_COMMAND_OK: + text = "Command okay."; + break; + case RESPONSE_220_SERVICE_READY: + text = "Service ready."; + break; + case RESPONSE_221_CLOSING_CONTROL_CONNECTION: + text = "Service closing control connection."; + break; + case RESPONSE_226_CLOSING_DATA_CONNECTION: + text = "Closing data connection."; + break; + case RESPONSE_230_USER_LOGGED_IN: + text = "User logged in, proceed."; + break; + case RESPONSE_331_PASSWORD_REQUIRED: + text = "Password required."; + break; + case RESPONSE_500_COMMAND_UNRECOGNIZED: + text = "Syntax error, command unrecognized."; + break; + case RESPONSE_502_COMMAND_NOT_IMPLEMENTED: + text = "Command not implemented."; + break; + case RESPONSE_503_BAD_SEQUENCE: + text = "Bad sequence of commands."; + break; + case RESPONSE_530_NOT_LOGGED_IN: + text = "Not logged in."; + break; + case RESPONSE_550_ACTION_NOT_TAKEN: + text = "Requested action not taken."; + break; + default: + break; + } + sendResponse(code, text); // Send the code AND the text to the FTP client. +} // FTPServer#sendResponse + + +/** + * Set the callbacks that are to be invoked to perform work. + * @param pCallbacks An instance of an FTPCallbacks based class. + */ +void FTPServer::setCallbacks(FTPCallbacks* pCallbacks) { + m_callbacks = pCallbacks; +} // FTPServer#setCallbacks + + +void FTPServer::setCredentials(std::string userid, std::string password) { + printf(">> setCredentials: userid=%s\n", userid.c_str()); + m_loginRequired = true; + m_userid = userid; + m_password = password; + printf("<< setCredentials\n"); +} // FTPServer#setCredentials + + +/** + * Set the TCP port we should listen on for FTP client requests. + */ +void FTPServer::setPort(uint16_t port) { + m_port = port; +} // FTPServer#setPort + + +/** + * Start being an FTP Server. + */ +void FTPServer::start() { + m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_serverSocket == -1) { + printf("socket: %s", strerror(errno)); + } + + int enable = 1; + setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_port); + int rc = bind(m_serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + printf("bind: %s\n", strerror(errno)); + } + rc = listen(m_serverSocket, 5); + if (rc == -1) { + printf("listen: %s", strerror(errno)); + } + while(1) { + waitForFTPClient(); + processCommand(); + } +} // FTPServer#start + + +/** + * Wait for a new client to connect. + */ +int FTPServer::waitForFTPClient() { + printf(">> FTPServer::waitForFTPClient\n"); + + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_clientSocket = accept(m_serverSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + + char ipAddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddr, sizeof(ipAddr)); + printf("Received connection from %s [%d]\n", ipAddr, clientAddress.sin_port); + + struct sockaddr_in socketAddressInfo; + unsigned int socketAddressInfoSize = sizeof(socketAddressInfo); + getsockname(m_clientSocket, (struct sockaddr*)&socketAddressInfo, &socketAddressInfoSize); + + inet_ntop(AF_INET, &socketAddressInfo.sin_addr, ipAddr, sizeof(ipAddr)); + printf("Connected at %s [%d]\n", ipAddr, socketAddressInfo.sin_port); + printf("<< FTPServer::waitForFTPClient: fd=%d\n", m_clientSocket); + + return m_clientSocket; +} // FTPServer::waitForFTPClient + diff --git a/networking/FTPServer/FTPServer.h b/networking/FTPServer/FTPServer.h new file mode 100644 index 00000000..7dc56f13 --- /dev/null +++ b/networking/FTPServer/FTPServer.h @@ -0,0 +1,137 @@ +/* + * FTPServer.h + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#ifndef NETWORKING_FTPSERVER_FTPSERVER_H_ +#define NETWORKING_FTPSERVER_FTPSERVER_H_ +#include +#include +#include +#include +#include + + +class FTPCallbacks { +public: + virtual void onStoreStart(std::string fileName); + virtual size_t onStoreData(uint8_t* data, size_t size); + virtual void onStoreEnd(); + virtual void onRetrieveStart(std::string fileName); + virtual size_t onRetrieveData(uint8_t *data, size_t size); + virtual void onRetrieveEnd(); + virtual std::string onDir(); + virtual ~FTPCallbacks(); +}; + +/** + * An implementation of FTPCallbacks that uses Posix File I/O to perform file access. + */ +class FTPFileCallbacks : public FTPCallbacks { +private: + std::ofstream m_storeFile; // File used to store data from the client. + std::ifstream m_retrieveFile; // File used to retrieve data for the client. + uint32_t m_byteCount; // Count of bytes sent over wire. +public: + void onStoreStart(std::string fileName) override; // Called for a STOR request. + size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. + void onStoreEnd() override; // Called at the end of a STOR request. + void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. + size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. + void onRetrieveEnd() override; // Called when we have retrieved all the data. + std::string onDir() override; // Called to retrieve all the directory entries. +}; + + +class FTPServer { +private: + int m_serverSocket; // The socket the FTP server is listening on. + int m_clientSocket; // The current client socket. + int m_dataSocket; // The data socket. + int m_passiveSocket; // The socket on which the server is listening for passive FTP connections. + uint16_t m_port; // The port the FTP server will use. + uint16_t m_dataPort; // The port for data connections. + uint32_t m_dataIp; // The ip address for data connections. + bool m_isPassive; // Are we in passive mode? If not, then we are in active mode. + bool m_isImage; // Are we in image mode? + size_t m_chunkSize; // The maximum chunk size. + std::string m_userid; // The required userid. + std::string m_password; // The required password. + std::string m_suppliedUserid; // The userid supplied from the USER command. + bool m_loginRequired; // Do we required a login? + bool m_isAuthenticated; // Have we authenticated? + std::string m_lastCommand; // The last command that was processed. + + FTPCallbacks* m_callbacks; // The callbacks for processing. + + void closeConnection(); + void closeData(); + void closePassive(); + void onAuth(std::istringstream& ss); + void onCwd(std::istringstream& ss); + void onList(std::istringstream& ss); + void onMkd(std::istringstream& ss); + void onNoop(std::istringstream& ss); + void onPass(std::istringstream& ss); + void onPasv(std::istringstream& ss); + void onPort(std::istringstream& ss); + void onPWD(std::istringstream& ss); + void onQuit(std::istringstream& ss); + void onRetr(std::istringstream& ss); + void onRmd(std::istringstream& ss); + void onStor(std::istringstream& ss); + void onSyst(std::istringstream& ss); + void onType(std::istringstream& ss); + void onUser(std::istringstream& ss); + void onXmkd(std::istringstream& ss); + void onXrmd(std::istringstream& ss); + + bool openData(); + + void receiveFile(std::string fileName); + void sendResponse(int code); + void sendResponse(int code, std::string text); + void sendData(uint8_t* pData, uint32_t size); + std::string listenPassive(); + int waitForFTPClient(); + void processCommand(); + +public: + FTPServer(); + virtual ~FTPServer(); + void setCredentials(std::string userid, std::string password); + void start(); + void setPort(uint16_t port); + void setCallbacks(FTPCallbacks* pFTPCallbacks); + static std::string getCurrentDirectory(); + class FileException: public std::exception { + + }; + + // Response codes. + static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; + static const int RESPONSE_200_COMMAND_OK = 200; + static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; + static const int RESPONSE_212_DIRECTORY_STATUS = 212; + static const int RESPONSE_213_FILE_STATUS = 213; + static const int RESPONSE_214_HELP_MESSAGE = 214; + static const int RESPONSE_220_SERVICE_READY = 220; + static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; + static const int RESPONSE_230_USER_LOGGED_IN = 230; + static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; + static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; + static const int RESPONSE_331_PASSWORD_REQUIRED = 331; + static const int RESPONSE_332_NEED_ACCOUNT = 332; + static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; + static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; + static const int RESPONSE_503_BAD_SEQUENCE = 503; + static const int RESPONSE_530_NOT_LOGGED_IN = 530; + static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; + static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; +}; + + + +#endif /* NETWORKING_FTPSERVER_FTPSERVER_H_ */ diff --git a/networking/FTPServer/README.md b/networking/FTPServer/README.md new file mode 100644 index 00000000..d5d570c3 --- /dev/null +++ b/networking/FTPServer/README.md @@ -0,0 +1,76 @@ +# ESP32 FTP Server +The world is awash in excellent FTP servers both as stand-alone applications as well as libraries that can be linked with to build ones own integrated FTP server. This project is yet another. + +The design intent is that it will be available to ESP32 based applications to server files from a Posix file system. + +## FTP Protocol +We will be concentrating exclsuively on the FTP Server protocol. This will allow an ESP32 to serve up files to FTP clients and allow FTP clients to deposit new files. + +The FTP Protocol is described in [RFC 959](https://tools.ietf.org/html/rfc959). + +Our server engine will implement the Server-PI (Server Protocol Interpreter) and the Server-DTP (Server Data Transfer Process). + +## Commands + +### PASV +The client has requested that the server enter passive mode. The response must be: + +``` +227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). +``` + +### PORT +Client requests that the server form a data connection to the given host and port. This is used in FTP *active* mode. +``` +PORT {h1,h2,h3,h4,p1,p2} +``` +port number is p1*256 + p2 + +### RETR +This command represents a request *from the client* to retrieve a file held by the server. + +### STOR +This command represents a request *from the client* to stor a file on the server. + +### SYST +Determine the System type of the FTP server. +``` +SYST +``` +The recommended response is: +``` +215 UNIX Type: L8 +``` + +### TYPE +Define the type of data to be transmitted. Codes include: + +* `A` - ASCII +* `E` - EBCDIC +* `I` - Image (binary) + +Example: +``` +TYPE I +``` + +### USER +Supply the user name that the client wishes to present to the FTP server. +``` +USER {name} +``` + +A response of `331` will request a password and should then be followed by the `PASS` command. + +## Active vs Passive +The FTP server can communicate data to the client in one of two modes called *active* and *passive*. In active mode, the FTP client is responsible for setting up a listening socket. When the FTP client is ready to receive data, it sends a `PORT` command informing the server of the IP address and port against which the FTP server should form the connection. +In passive mode, the FTP client sends a `PASV` command to the FTP server. The FTP server then responds with a host and port pair which will be listened upon to receive a connection request *from* the client. Once received, the data can then flow over this connection. + +See also: + +* [Active FTP vs. Passive FTP, a Definitive Explanation](http://slacksite.com/other/ftp.html) + +# References + +* [FTP: File Transfer Protocol](https://cr.yp.to/ftp.html) +* [Wikipedia: File Transfer Protocol](https://en.wikipedia.org/wiki/File_Transfer_Protocol) \ No newline at end of file diff --git a/networking/FTPServer/main.cpp b/networking/FTPServer/main.cpp new file mode 100644 index 00000000..45725051 --- /dev/null +++ b/networking/FTPServer/main.cpp @@ -0,0 +1,12 @@ +#include "FTPServer.h" +#include + + +int main(int argc, char* argv[]) { + printf("FTPServer starting!\n"); + FTPServer *ftpServer = new FTPServer(); + ftpServer->setCallbacks(new FTPFileCallbacks()); + //ftpServer->setCredentials("user", "pass"); + ftpServer->setPort(9876); + ftpServer->start(); +} From 937a81c18d742e86305df5184ccfb70f35b77c85 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Fri, 8 Jun 2018 11:35:44 -0500 Subject: [PATCH 218/310] Work in progress for DMA driver --- memory/dma/DMA.cpp | 411 +++++++++++++++++++++++++++++++++++++ memory/dma/DMA.h | 26 +++ memory/dma/DMA_GPIO.h | 13 ++ memory/dma/GPIODriver.cpp | 88 ++++++++ memory/dma/GPIODriver.h | 18 ++ memory/dma/dma_console.cpp | 215 +++++++++++++++++++ memory/dma/main.cpp | 14 ++ 7 files changed, 785 insertions(+) create mode 100644 memory/dma/DMA.cpp create mode 100644 memory/dma/DMA.h create mode 100644 memory/dma/DMA_GPIO.h create mode 100644 memory/dma/GPIODriver.cpp create mode 100644 memory/dma/GPIODriver.h create mode 100644 memory/dma/dma_console.cpp create mode 100644 memory/dma/main.cpp diff --git a/memory/dma/DMA.cpp b/memory/dma/DMA.cpp new file mode 100644 index 00000000..a72a92e5 --- /dev/null +++ b/memory/dma/DMA.cpp @@ -0,0 +1,411 @@ +/* + * DMA.cpp + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#include "DMA.h" +#include +#include +#include +#include + +#include // Inclusions for WRITE_PERI_xxx and family. +#include // Inclusions for I2S registers. +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define D0 GPIO_NUM_4 +#define D1 GPIO_NUM_5 +#define D2 GPIO_NUM_18 +#define D3 GPIO_NUM_19 +#define D4 GPIO_NUM_36 +#define D5 GPIO_NUM_39 +#define D6 GPIO_NUM_34 +#define D7 GPIO_NUM_35 +#define VSYNC GPIO_NUM_25 +#define HREF GPIO_NUM_23 +#define PCLK GPIO_NUM_22 + +lldesc_t ll1; + +QueueHandle_t queueHandle; +intr_handle_t intHandle; + +class LogInterruptTask : public Task { + void run(void *data) { + printf("Starting interrupt log\n"); + uint32_t queueItem; + while(1) { + xQueueReceive(queueHandle, &queueItem, portMAX_DELAY); + printf("New interrupt value: 0x%x\n", queueItem); + } // End while 1 + } // LogInterruptTask#run +}; // LogInterruptTask + +static void IRAM_ATTR dmaInt(void *arg) { + uint32_t value = 0x99; + xQueueSendToBackFromISR(queueHandle, &value, nullptr); + I2S0.int_clr.val = I2S0.int_raw.val; +} + + +DMA::DMA() { + +} + +DMA::~DMA() { +} + + +void DMA::clearInterupts() { + + // Set 1 bits in the flags for I2S_INT_CLR_REG to clear interrupts. + I2S0.int_clr.val = 0xFFFFFFFF; // 32 bits of 1 to clear all flags. + +} // DMA#clearInterupts + + +void DMA::reset() { + + clearInterupts(); + ll1.eof = 1; + ll1.length = 0; // Number of valid bytes. + ll1.size = 1024; // Size of buffer. + ll1.offset = 0; + ll1.sosf = 0; + ll1.empty = 0; // No next link + ll1.owner = 1; // 1 = DMA, 2 = software + memset((void *)ll1.buf, 0xff, ll1.size); + + + + const uint32_t lc_conf_reset_flags = I2S_IN_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M; + I2S0.lc_conf.val |= lc_conf_reset_flags; + I2S0.lc_conf.val &= ~lc_conf_reset_flags; + + + // See TRM 12.4.2. + const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M; + I2S0.conf.val |= conf_reset_flags; + I2S0.conf.val &= ~conf_reset_flags; + + while (I2S0.state.rx_fifo_reset_back) { // Bit is 1 while not ready + ; + } + + I2S0.in_link.addr = (uint32_t)&ll1; +} // DMA#reset + + +void DMA::dumpBuffer() { + printf(">> dumpBuffer\n"); + GeneralUtils::hexDump((uint8_t*)ll1.buf, 64); + printf("<< dumpBuffer\n"); +} + +void DMA::setCameraMode() { + printf(">> setCameraMode\n"); + + gpio_pad_select_gpio(D0); + gpio_pad_select_gpio(D1); + gpio_pad_select_gpio(D2); + gpio_pad_select_gpio(D3); + gpio_pad_select_gpio(D4); + gpio_pad_select_gpio(D5); + gpio_pad_select_gpio(D6); + gpio_pad_select_gpio(D7); + gpio_pad_select_gpio(VSYNC); + gpio_pad_select_gpio(HREF); + gpio_pad_select_gpio(PCLK); + + gpio_set_direction(D0, GPIO_MODE_INPUT); + gpio_set_direction(D1, GPIO_MODE_INPUT); + gpio_set_direction(D2, GPIO_MODE_INPUT); + gpio_set_direction(D3, GPIO_MODE_INPUT); + gpio_set_direction(D4, GPIO_MODE_INPUT); + gpio_set_direction(D5, GPIO_MODE_INPUT); + gpio_set_direction(D6, GPIO_MODE_INPUT); + gpio_set_direction(D7, GPIO_MODE_INPUT); + gpio_set_direction(VSYNC, GPIO_MODE_INPUT); + gpio_set_direction(HREF, GPIO_MODE_INPUT); + gpio_set_direction(PCLK, GPIO_MODE_INPUT); + + // Map sources / sinks of data to their logical counter parts. + gpio_matrix_in(D0, I2S0I_DATA_IN0_IDX, false); + gpio_matrix_in(D1, I2S0I_DATA_IN1_IDX, false); + gpio_matrix_in(D2, I2S0I_DATA_IN2_IDX, false); + gpio_matrix_in(D3, I2S0I_DATA_IN3_IDX, false); + gpio_matrix_in(D4, I2S0I_DATA_IN4_IDX, false); + gpio_matrix_in(D5, I2S0I_DATA_IN5_IDX, false); + gpio_matrix_in(D6, I2S0I_DATA_IN6_IDX, false); + gpio_matrix_in(D7, I2S0I_DATA_IN7_IDX, false); + + // Set constants for the bits [15:8] alternating 1 and 0. This results in 0xAA. + gpio_matrix_in(0x30, I2S0I_DATA_IN8_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN9_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN10_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN11_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN12_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN13_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN14_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN15_IDX, false); + + gpio_matrix_in(0x38 /*VSYNC*/, I2S0I_V_SYNC_IDX, false); + gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false); // Constant high + gpio_matrix_in(0x38 /* HREF */, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(PCLK, I2S0I_WS_IN_IDX, false); + + //SET_PERI_REG_BITS(I2SCONF2_REG, I2S_CAMERA_EN, 1, I2S_CAMERA_EN_S); + + + // We must enable I2S0 peripheral + periph_module_enable(PERIPH_I2S0_MODULE); + + reset(); + + + /* + rx_msb_right | result | +---------------+--------------------------------------------------------------------------------+ + 0 | 00 00 v1low v1high 00 00 v2low v2high 00 00 v3low v3high 00 00 v4low v4high | + 1 | 00 00 v1low v1high 00 00 v2low ?? 00 00 v2low ?? 00 00 v3 low ?? | + */ + + // We enable I2S_CONF2_REG:I2S_CAMERA_EN + I2S0.conf2.camera_en = 1; // 1 for camera mode (TRM). + + // We enable I2S_CONF_REG:I2S_LCD_EN + I2S0.conf2.lcd_en = 1; // 1 for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_SLAVE_MOD + I2S0.conf.rx_slave_mod = 1; // 1 for slave receiving mode. Required for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_MSB_RIGHT + I2S0.conf.rx_msb_right = 0; // 0 required for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_RIGHT_FIRST + I2S0.conf.rx_right_first = 0; // 0 required for camera mode (TRM). + + // I2S_CONF_CHANG_REG:I2S_RX_CHAN_MOD + I2S0.conf_chan.rx_chan_mod = 1; // 1 required for camera mode (TRM). + + // I2S_FIFO_CONF_REG:I2S_RX_FIFO_MOD + I2S0.fifo_conf.rx_fifo_mod = 1; // 1 = 16bit single channel data. Required for camera mode (TRM). + + // Configure clock divider + I2S0.clkm_conf.clkm_div_a = 1; + I2S0.clkm_conf.clkm_div_b = 0; + I2S0.clkm_conf.clkm_div_num = 2; + + // FIFO will sink data to DMA + I2S0.fifo_conf.dscr_en = 1; + + // FIFO configuration + I2S0.fifo_conf.rx_fifo_mod = 2; + I2S0.fifo_conf.rx_fifo_mod_force_en = 1; + I2S0.conf_chan.rx_chan_mod = 1; + + // Clear flags which are used in I2S serial mode + I2S0.sample_rate_conf.rx_bits_mod = 0; + I2S0.conf.rx_right_first = 0; + I2S0.conf.rx_msb_right = 0; + I2S0.conf.rx_msb_shift = 0; + I2S0.conf.rx_mono = 0; + I2S0.conf.rx_short_sync = 0; + I2S0.timing.val = 0; + + + esp_err_t errRc; + errRc = esp_intr_alloc( + ETS_I2S0_INTR_SOURCE, + ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, + &dmaInt, + NULL, + &intHandle); + if (errRc != ESP_OK) { + printf("esp_intr_alloc: %d\n", errRc); + } + + errRc = esp_intr_enable(intHandle); + if (errRc != ESP_OK) { + printf("esp_intr_enable: %d\n", errRc); + } + + I2S0.rx_eof_num = 8; + + + ll1.eof = 1; + ll1.length = 0; // Number of valid bytes. + ll1.size = 1024; // Size of buffer. + ll1.offset = 0; + ll1.sosf = 0; + ll1.empty = 0; // No next link + ll1.owner = 1; // 1 = DMA, 0 = software + + memset((void *)ll1.buf, 0xff, ll1.size); + + // The address of the first in-link descriptor. + I2S0.in_link.addr = (uint32_t)&ll1; + + // Set to 1 to start in-link descriptor. + //I2S0.in_link.start = 1; + + startRX(); + + clearInterupts(); + + printf("<< setCameraMode\n"); +} // DMA#setCameraMode + + +void DMA::stopRX() { + // Set to 0 to stop receiving data. + I2S0.conf.rx_start = 0; + I2S0.conf.rx_reset = 1; +} // DMA#stopRX + + +void DMA::start() { + ll1.buf = (uint8_t*)malloc(1024); // Allocate storage for the linked list buffer. + queueHandle = xQueueCreate(50, sizeof(uint32_t)); + + LogInterruptTask* pLogInterruptTask = new LogInterruptTask(); + pLogInterruptTask->start(); +} // DMA#start + + +void DMA::startRX() { + // Set to 1 to start receiving data. + + printf(">> DMA::startRX\n"); + + I2S0.int_clr.val = I2S0.int_raw.val; + + I2S0.rx_eof_num = 8; + + I2S0.in_link.addr = (uint32_t)&ll1; + //I2S0.in_link.stop = 1; + I2S0.in_link.start = 1; + //I2S0.in_link.restart = 1; + I2S0.conf.rx_start = 1; + + // Enable interrupt generation + I2S0.int_ena.in_suc_eof = true; + //I2S0.int_ena.in_done = true; + /* + I2S0.int_ena.in_dscr_empty = true; + I2S0.int_ena.in_dscr_err = true; + I2S0.int_ena.in_err_eof = true; + + I2S0.int_ena.rx_hung = true; + I2S0.int_ena.rx_rempty = true; + I2S0.int_ena.rx_take_data = true; + I2S0.int_ena.rx_wfull = true; + */ + //I2S0.int_ena.rx_take_data = true; + + + printf("<< DMA::startRX\n"); +} // DMA#startRX + + +void DMA::dumpStatus() { + printf("conf\n"); + printf("%-20s: %d\n", "I2S_RX_SLAVE_MOD", I2S0.conf.rx_slave_mod); + printf("%-20s: %d\n", "I2S_RX_MSB_RIGHT", I2S0.conf.rx_msb_right); + printf("%-20s: %d\n", "I2S_RX_RIGHT_FIRST", I2S0.conf.rx_right_first); + printf("%-20s: %d\n", "I2S_RX_START", I2S0.conf.rx_start); + printf("%-20s: %d\n", "I2S_RX_RESET", I2S0.conf.rx_reset); + printf("-----\n"); + + printf("conf_chan\n"); + printf("%-20s: %d\n", "I2S_RX_CHAN_MOD", I2S0.conf_chan.rx_chan_mod); + printf("-----\n"); + + printf("fifo_conf (I2S_FIFO_CONF_REG)\n"); + printf("%-26s: %d\n", "I2S_RX_FIFO_MOD_FORCE_EN", I2S0.fifo_conf.rx_fifo_mod_force_en); + printf("%-26s: %d\n", "I2S_RX_FIFO_MOD", I2S0.fifo_conf.rx_fifo_mod); + printf("%-26s: %d\n", "I2S_RX_DSCR_EN", I2S0.fifo_conf.dscr_en); + printf("%-26s: %d\n", "I2S_RX_DATA_NUM", I2S0.fifo_conf.rx_data_num); + printf("-----\n"); + + printf("conf2\n"); + printf("%-20s: %d\n", "I2S_CAMERA_EN", I2S0.conf2.camera_en); + printf("%-20s: %d\n", "I2S_LCD_EN", I2S0.conf2.lcd_en); + printf("-----\n"); + + + printf("state\n"); + printf("%-20s: %d\n", "I2S_RX_FIFO_RESET_BACK", I2S0.state.rx_fifo_reset_back); + printf("-----\n"); + + printf("rx_eof_num (I2S_RXEOF_NUM_REG)\n"); + printf("%-20s: %d\n", "I2S_RXEOF_NUM_REG", I2S0.rx_eof_num); + printf("-----\n"); + + printf("in_eof_des_addr (I2S_IN_EOF_DES_ADDR_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_IN_EOF_DES_ADDR_REG", I2S0.in_eof_des_addr); + printf("-----\n"); + + printf("in_link_dscr (I2S_INLINK_DSCR_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_REG", I2S0.in_link_dscr); + printf("-----\n"); + + printf("in_link_dscr_bf0 (I2S_INLINK_DSCR_BF0_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_BF0_REG", I2S0.in_link_dscr_bf0); + printf("-----\n"); + + printf("in_link_dscr_bf1 (I2S_INLINK_DSCR_BF1_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_BF1_REG", I2S0.in_link_dscr_bf1); + printf("-----\n"); + + printf("int_raw\n"); + printf("%-25s: %d\n", "I2S_IN_DSCR_EMPTY_INT_RAW", I2S0.int_raw.in_dscr_empty); + printf("%-25s: %d\n", "I2S_IN_DSCR_ERR_INT_RAW", I2S0.int_raw.in_dscr_err); + printf("%-25s: %d\n", "I2S_IN_SUC_EOF_INT_RAW", I2S0.int_raw.in_suc_eof); + printf("%-25s: %d\n", "I2S_IN_ERR_EOF_INT_RAW", I2S0.int_raw.in_err_eof); + printf("%-25s: %d\n", "I2S_IN_DONE_INT_RAW", I2S0.int_raw.in_done); + printf("%-25s: %d\n", "I2S_RX_HUNG_INT_RAW", I2S0.int_raw.rx_hung); + printf("%-25s: %d\n", "I2S_RX_REMPTY_INT_RAW", I2S0.int_raw.rx_rempty); + printf("%-25s: %d\n", "I2S_RX_WFULL_INT_RAW", I2S0.int_raw.rx_wfull); + printf("%-25s: %d\n", "I2S_RX_TAKE_DATA_INT_RAW", I2S0.int_raw.rx_take_data); + printf("-----\n"); + + printf("int_ena\n"); + printf("%-25s: %d\n", "I2S_IN_DSCR_EMPTY_INT_RAW", I2S0.int_ena.in_dscr_empty); + printf("%-25s: %d\n", "I2S_IN_DSCR_ERR_INT_RAW", I2S0.int_ena.in_dscr_err); + printf("%-25s: %d\n", "I2S_IN_SUC_EOF_INT_RAW", I2S0.int_ena.in_suc_eof); + printf("%-25s: %d\n", "I2S_IN_ERR_EOF_INT_RAW", I2S0.int_ena.in_err_eof); + printf("%-25s: %d\n", "I2S_IN_DONE_INT_RAW", I2S0.int_ena.in_done); + printf("%-25s: %d\n", "I2S_RX_HUNG_INT_RAW", I2S0.int_ena.rx_hung); + printf("%-25s: %d\n", "I2S_RX_REMPTY_INT_RAW", I2S0.int_ena.rx_rempty); + printf("%-25s: %d\n", "I2S_RX_WFULL_INT_RAW", I2S0.int_ena.rx_wfull); + printf("%-25s: %d\n", "I2S_RX_TAKE_DATA_INT_RAW", I2S0.int_ena.rx_take_data); + printf("-----\n"); + + printf("in_link\n"); + printf("%-20s: %d\n", "I2S_INLINK_PARK", I2S0.in_link.park); + printf("%-20s: %d\n", "I2S_INLINK_RESTART", I2S0.in_link.restart); + printf("%-20s: %d\n", "I2S_INLINK_START", I2S0.in_link.start); + printf("%-20s: %d\n", "I2S_INLINK_STOP", I2S0.in_link.stop); + printf("%-20s: 0x%x\n", "I2S_INLINK_ADDR", (I2S0.in_link.addr & 0xFFFFF)); + printf("-----\n"); + + printf("lldesc 1: 0x%x\n", (uint32_t)&ll1); + printf("%-20s: %d\n", "length", ll1.length); + printf("%-20s: %d\n", "size", ll1.size); + printf("%-20s: %d\n", "owner", ll1.owner); + printf("%-20s: %d\n", "empty", ll1.empty); + printf("%-20s: 0x%x\n", "&buf", (uint32_t)ll1.buf); +} // DMA#dumpStatus diff --git a/memory/dma/DMA.h b/memory/dma/DMA.h new file mode 100644 index 00000000..2be79458 --- /dev/null +++ b/memory/dma/DMA.h @@ -0,0 +1,26 @@ +/* + * DMA.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_DMA_H_ +#define MAIN_DMA_H_ + +class DMA { +public: + DMA(); + virtual ~DMA(); + void clearInterupts(); + void dumpBuffer(); + void dumpStatus(); + void reset(); + void setCameraMode(); + void start(); + void startRX(); + void stopRX(); + +}; + +#endif /* MAIN_DMA_H_ */ diff --git a/memory/dma/DMA_GPIO.h b/memory/dma/DMA_GPIO.h new file mode 100644 index 00000000..124adc28 --- /dev/null +++ b/memory/dma/DMA_GPIO.h @@ -0,0 +1,13 @@ +/* + * DMA_GPIO.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_DMA_GPIO_H_ +#define MAIN_DMA_GPIO_H_ + + + +#endif /* MAIN_DMA_GPIO_H_ */ diff --git a/memory/dma/GPIODriver.cpp b/memory/dma/GPIODriver.cpp new file mode 100644 index 00000000..78021c0d --- /dev/null +++ b/memory/dma/GPIODriver.cpp @@ -0,0 +1,88 @@ +/* + * GPIODriver.cpp + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#include "GPIODriver.h" +#include "DMA_GPIO.h" +#include +#include +#include + +#define D0 GPIO_NUM_4 +#define D1 GPIO_NUM_5 +#define D2 GPIO_NUM_18 +#define D3 GPIO_NUM_19 +#define D4 GPIO_NUM_13 // 36 +#define D5 GPIO_NUM_14 // 39 +#define D6 GPIO_NUM_15 // 34 +#define D7 GPIO_NUM_16 // 35 +#define VSYNC GPIO_NUM_25 +#define HREF GPIO_NUM_23 +#define PCLK GPIO_NUM_22 + +GPIODriver::GPIODriver() { +} + +GPIODriver::~GPIODriver() { +} + +void GPIODriver::run() { + printf(">> GPIODriver::run\n"); + // Setup the GPIO pins as output. + gpio_pad_select_gpio(D0); + gpio_pad_select_gpio(D1); + gpio_pad_select_gpio(D2); + gpio_pad_select_gpio(D3); + gpio_pad_select_gpio(D4); + gpio_pad_select_gpio(D5); + gpio_pad_select_gpio(D6); + gpio_pad_select_gpio(D7); + gpio_pad_select_gpio(VSYNC); + gpio_pad_select_gpio(HREF); + gpio_pad_select_gpio(PCLK); + + gpio_set_direction(D0, GPIO_MODE_OUTPUT); + gpio_set_direction(D1, GPIO_MODE_OUTPUT); + gpio_set_direction(D2, GPIO_MODE_OUTPUT); + gpio_set_direction(D3, GPIO_MODE_OUTPUT); + gpio_set_direction(D4, GPIO_MODE_OUTPUT); + gpio_set_direction(D5, GPIO_MODE_OUTPUT); + gpio_set_direction(D6, GPIO_MODE_OUTPUT); + gpio_set_direction(D7, GPIO_MODE_OUTPUT); + gpio_set_direction(VSYNC, GPIO_MODE_OUTPUT); + gpio_set_direction(HREF, GPIO_MODE_OUTPUT); + gpio_set_direction(PCLK, GPIO_MODE_OUTPUT); + + gpio_set_level(VSYNC, 1); + gpio_set_level(HREF, 1); + // Initialize the counter. + uint8_t counter = 0; + // Loop + + gpio_set_level(PCLK, 0); + while(1) { + + // Write the counter value to the GPIOs + gpio_set_level(D0, (counter & 0b00000001) != 0); + gpio_set_level(D1, (counter & 0b00000010) != 0); + gpio_set_level(D2, (counter & 0b00000100) != 0); + gpio_set_level(D3, (counter & 0b00001000) != 0); + gpio_set_level(D4, (counter & 0b00010000) != 0); + gpio_set_level(D5, (counter & 0b00100000) != 0); + gpio_set_level(D6, (counter & 0b01000000) != 0); + gpio_set_level(D7, (counter & 0b10000000) != 0); + + + // Pulse the clock + gpio_set_level(PCLK, 1); + vTaskDelay(10/portTICK_PERIOD_MS); + gpio_set_level(PCLK, 0); + + // Delay + vTaskDelay(990/portTICK_PERIOD_MS); + counter++; + } // End while 1 +} // GPIODriver#run diff --git a/memory/dma/GPIODriver.h b/memory/dma/GPIODriver.h new file mode 100644 index 00000000..03f8d7ec --- /dev/null +++ b/memory/dma/GPIODriver.h @@ -0,0 +1,18 @@ +/* + * GPIODriver.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_GPIODRIVER_H_ +#define MAIN_GPIODRIVER_H_ + +class GPIODriver { +public: + GPIODriver(); + virtual ~GPIODriver(); + void run(); +}; + +#endif /* MAIN_GPIODRIVER_H_ */ diff --git a/memory/dma/dma_console.cpp b/memory/dma/dma_console.cpp new file mode 100644 index 00000000..bc3bd05b --- /dev/null +++ b/memory/dma/dma_console.cpp @@ -0,0 +1,215 @@ +/** + * DMA Driver + * + * @author Neil Kolban + * @date 2018-05-26 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "DMA.h" +#include "GPIODriver.h" +#include + +DMA dma; + + +struct arg_end* reset_arg_end = arg_end(10); +void *reset_argtable[] = { + reset_arg_end +}; + +int runReset(int argc, char *argv[]) { + printf("Reset\n"); + dma.reset(); + return 0; +} + + +struct arg_end* start_arg_end = arg_end(10); +void *start_argtable[] = { + start_arg_end +}; + +int runStart(int argc, char *argv[]) { + printf("Started\n"); + return 0; +} + + +struct arg_end* startRX_arg_end = arg_end(10); +void *startRX_argtable[] = { + startRX_arg_end +}; + +int runStartRX(int argc, char *argv[]) { + printf("StartRX\n"); + return 0; +} + + +struct arg_end* stopRX_arg_end = arg_end(10); +void *stopRX_argtable[] = { + stopRX_arg_end +}; + +int runStopRX(int argc, char *argv[]) { + printf("StopRX\n"); + return 0; +} + + +struct arg_end* hexdump_arg_end = arg_end(10); +void *hexdump_argtable[] = { + hexdump_arg_end +}; + +int runHexdump(int argc, char *argv[]) { + printf("Hexdump\n"); + dma.dumpBuffer(); + return 0; +} + +struct arg_end* clientStart_arg_end = arg_end(10); +void *clientStart_arg_end_argtable[] = { + clientStart_arg_end +}; + +int runClientStart(int argc, char *argv[]) { + printf("runClientStart\n"); + GPIODriver gpioDriver; + gpioDriver.run(); + return 0; +} + + +struct arg_end* camera_arg_end = arg_end(10); +void *camera_argtable[] = { + camera_arg_end +}; + +int runCamera(int argc, char *argv[]) { + printf("camera\n"); + dma.setCameraMode(); + return 0; +} + +struct arg_end* stop_arg_end = arg_end(10); +void *stop_argtable[] = { + stop_arg_end +}; + +int runStop(int argc, char *argv[]) { + printf("Stopped\n"); + return 0; +} + + +struct arg_end* status_arg_end = arg_end(10); +void *status_argtable[] = { + stop_arg_end +}; + +int runStatus(int argc, char *argv[]) { + printf("Status\n"); + dma.dumpStatus(); + return 0; +} + + +void test_console(void) +{ + dma.start(); + // Boiler plate setup for using linenoise + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0); + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + esp_console_config_t consoleConfig; + consoleConfig.max_cmdline_args = 5; + consoleConfig.max_cmdline_length = 100; + + esp_console_init(&consoleConfig); + esp_console_register_help_command(); + + esp_console_cmd_t consoleCmd; + consoleCmd.command = "start"; + consoleCmd.func = runStart; + consoleCmd.help = "Start processing"; + consoleCmd.argtable = start_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "stop"; + consoleCmd.func = runStop; + consoleCmd.help = "Stop processing"; + consoleCmd.argtable = stop_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "status"; + consoleCmd.func = runStatus; + consoleCmd.help = "Status processing"; + consoleCmd.argtable = status_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "camera"; + consoleCmd.func = runCamera; + consoleCmd.help = "camera processing"; + consoleCmd.argtable = camera_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "clientStart"; + consoleCmd.func = runClientStart; + consoleCmd.help = "clientStart processing"; + consoleCmd.argtable = clientStart_arg_end_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "hexdump"; + consoleCmd.func = runHexdump; + consoleCmd.help = "hexdump processing"; + consoleCmd.argtable = hexdump_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "startRX"; + consoleCmd.func = runStartRX; + consoleCmd.help = "startRX processing"; + consoleCmd.argtable = startRX_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "stopRX"; + consoleCmd.func = runStopRX; + consoleCmd.help = "stopRX processing"; + consoleCmd.argtable = stopRX_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "reset"; + consoleCmd.func = runReset; + consoleCmd.help = "reset processing"; + consoleCmd.argtable = reset_argtable; + esp_console_cmd_register(&consoleCmd); + + + //linenoiseClearScreen(); + linenoiseSetMultiLine(0); + + + while(1) { + char* line = linenoise("Enter command > "); + if (line != NULL) { + int ret; + esp_console_run(line, &ret); + linenoiseHistoryAdd(line); + linenoiseFree(line); + } // Line is not null + printf("--------------\n"); + } // End while loop +} diff --git a/memory/dma/main.cpp b/memory/dma/main.cpp new file mode 100644 index 00000000..f5715f5d --- /dev/null +++ b/memory/dma/main.cpp @@ -0,0 +1,14 @@ +extern "C" { + void app_main(void); +} + +extern void test_linenoise(); +extern void test_argtable(); +extern void test_console(); + +void app_main(void) +{ + //test_linenoise(); + //test_argtable(); + test_console(); +} From 75d405cac70c1e046445ec098d4654bc1b47f8c2 Mon Sep 17 00:00:00 2001 From: Han Date: Thu, 14 Jun 2018 22:10:52 +0200 Subject: [PATCH 219/310] Set spi_bus_config_t and spi_device_interface_config_t flags --- cpp_utils/SPI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp_utils/SPI.cpp b/cpp_utils/SPI.cpp index 56b87502..5ce45d15 100644 --- a/cpp_utils/SPI.cpp +++ b/cpp_utils/SPI.cpp @@ -54,6 +54,7 @@ void SPI::init(int mosiPin, int misoPin, int clkPin, int csPin) { bus_config.quadwp_io_num = -1; // Not used bus_config.quadhd_io_num = -1; // Not used bus_config.max_transfer_sz = 0; // 0 means use default. + bus_config.flags = (SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO); ESP_LOGI(LOG_TAG, "... Initializing bus; host=%d", m_host); @@ -78,7 +79,7 @@ void SPI::init(int mosiPin, int misoPin, int clkPin, int csPin) { dev_config.cs_ena_pretrans = 0; dev_config.clock_speed_hz = 100000; dev_config.spics_io_num = csPin; - dev_config.flags = 0; + dev_config.flags = SPI_DEVICE_NO_DUMMY; dev_config.queue_size = 1; dev_config.pre_cb = NULL; dev_config.post_cb = NULL; From f77854155291af610e8f430f85c1cbb5e989cbf2 Mon Sep 17 00:00:00 2001 From: Oleg Date: Sun, 17 Jun 2018 22:05:19 +0300 Subject: [PATCH 220/310] splitted BLEScan::start to blocking & non-blocking oveloads --- cpp_utils/BLEScan.cpp | 29 ++++++++++++++++++----------- cpp_utils/BLEScan.h | 3 ++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 73086c6e..3046b7c8 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -189,16 +189,14 @@ void BLEScan::setWindow(uint16_t windowMSecs) { /** * @brief Start scanning. * @param [in] duration The duration in seconds for which to scan. - * @param [in] scanCompleteCB A function to be called when scanning has completed. This can - * be supplied as nullptr (the default) in which case the call to start will block until scanning has - * been completed. - * @return The BLEScanResults. Only applicable if we are waiting for results. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @return True if scan started or false if there was an error. */ -BLEScanResults BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); - m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. m_semaphoreScanEnd.take(std::string("start")); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. m_scanResults.m_vectorAdvertisedDevices.clear(); @@ -207,7 +205,7 @@ BLEScanResults BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanR if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); m_semaphoreScanEnd.give(); - return m_scanResults; + return false; } errRc = ::esp_ble_gap_start_scanning(duration); @@ -215,16 +213,25 @@ BLEScanResults BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanR if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); m_semaphoreScanEnd.give(); - return m_scanResults; + return false; } m_stopped = false; - if (m_scanCompleteCB == nullptr) { + ESP_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +BLEScanResults BLEScan::start(uint32_t duration) { + if(start(duration, nullptr)) { m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. } - - ESP_LOGD(LOG_TAG, "<< start()"); return m_scanResults; } // start diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index 3e53ce64..76c7c7cc 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -53,7 +53,8 @@ class BLEScan { bool wantDuplicates = false); void setInterval(uint16_t intervalMSecs); void setWindow(uint16_t windowMSecs); - BLEScanResults start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults) = nullptr); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); + BLEScanResults start(uint32_t duration); void stop(); private: From eead14aff146a23f74f40308d3048e2b2b3249f6 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 19 Jun 2018 08:36:58 +0200 Subject: [PATCH 221/310] Add remove service --- cpp_utils/BLEServer.cpp | 8 ++++++++ cpp_utils/BLEServer.h | 3 ++- cpp_utils/BLEServiceMap.cpp | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index bc079813..03696ea5 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -323,6 +323,14 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { m_pServerCallbacks = pCallbacks; } // setCallbacks +/* + * Remove service + */ +void BLEServer::removeService(BLEService *service) { + esp_ble_gatts_delete_service(handle); + uint16_t handle = service->getHandle(); + m_serviceMap->removeService(service); +} /** * @brief Start advertising. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 6c71679a..585fdf89 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -42,6 +42,7 @@ class BLEServiceMap { std::string toString(); BLEService* getFirst(); BLEService* getNext(); + void removeService(BLEService *service); private: std::map m_handleMap; @@ -61,7 +62,7 @@ class BLEServer { BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); - + void removeService(BLEService *service); private: BLEServer(); diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index 96811ad2..0184f0da 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -120,4 +120,9 @@ BLEService* BLEServiceMap::getNext() { return pRet; } // getNext +void BLEServiceMap::removeService(BLEService *service){ + m_handleMap->erase(serice->getHandle()); + m_uuidMap->erase(service); +} + #endif /* CONFIG_BT_ENABLED */ From 21ba4a8df9e4c535193b768d8acf7dee5b2ca61d Mon Sep 17 00:00:00 2001 From: James Bergin Date: Wed, 20 Jun 2018 16:18:36 -0500 Subject: [PATCH 222/310] - Added way to remove services. --- cpp_utils/BLEServer.cpp | 8 +++++ cpp_utils/BLEServer.h | 2 ++ cpp_utils/BLEService.cpp | 60 +++++++++++++++++++++++++++++++++++++ cpp_utils/BLEService.h | 4 +++ cpp_utils/BLEServiceMap.cpp | 5 ++++ 5 files changed, 79 insertions(+) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index bc079813..d5c9de68 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -323,6 +323,14 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { m_pServerCallbacks = pCallbacks; } // setCallbacks +/* + * Remove service + */ +void BLEServer::removeService(BLEService *service) { + service->stop(); + service->executeDelete(); + m_serviceMap.removeService(service); +} /** * @brief Start advertising. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 6c71679a..95c55d53 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -42,6 +42,7 @@ class BLEServiceMap { std::string toString(); BLEService* getFirst(); BLEService* getNext(); + void removeService(BLEService *service); private: std::map m_handleMap; @@ -61,6 +62,7 @@ class BLEServer { BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); + void removeService(BLEService *service); private: diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 6636ca5c..d7a4da27 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -91,6 +91,29 @@ void BLEService::executeCreate(BLEServer *pServer) { } // executeCreate +/** + * @brief Delete the service. + * Delete the service. + * @param [in] gatts_if The handle of the GATT server interface. + * @return N/A. + */ + +void BLEService::executeDelete() { + ESP_LOGD(LOG_TAG, ">> executeDelete()"); + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + + esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreDeleteEvt.wait("executeDelete"); + ESP_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + /** * @brief Dump details of this BLE GATT service. * @return N/A. @@ -153,6 +176,34 @@ void BLEService::start() { } // start +/** + * @brief Stop the service. + * @return Stop the service. + */ +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start + + /** * @brief Set the handle associated with this service. * @param [in] handle The handle associated with the service. @@ -278,6 +329,10 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_START_EVT + case ESP_GATTS_STOP_EVT: + m_semaphoreStopEvt.give(); + break; + // ESP_GATTS_CREATE_EVT // Called when a new service is registered as having been created. @@ -299,6 +354,11 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_CREATE_EVT + case ESP_GATTS_DELETE_EVT: { + m_semaphoreDeleteEvt.give(); + break; + } + default: { break; } // Default diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 79958f3d..93b4b2c6 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -57,11 +57,13 @@ class BLEService { BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); void dump(); void executeCreate(BLEServer* pServer); + void executeDelete(); BLECharacteristic* getCharacteristic(const char* uuid); BLECharacteristic* getCharacteristic(BLEUUID uuid); BLEUUID getUUID(); BLEServer* getServer(); void start(); + void stop(); std::string toString(); uint16_t getHandle(); uint8_t m_id = 0; @@ -82,7 +84,9 @@ class BLEService { BLEUUID m_uuid; FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); uint32_t m_numHandles; diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index 96811ad2..78f00feb 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -120,4 +120,9 @@ BLEService* BLEServiceMap::getNext() { return pRet; } // getNext +void BLEServiceMap::removeService(BLEService *service){ + m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} + #endif /* CONFIG_BT_ENABLED */ From 07c4b8220e6c5240acc1492ba286d25dbaf993e0 Mon Sep 17 00:00:00 2001 From: James Bergin Date: Thu, 21 Jun 2018 10:46:36 -0500 Subject: [PATCH 223/310] - Updated comments for documentation. --- cpp_utils/BLEService.cpp | 30 ++++++++++++++++++++++++------ cpp_utils/BLEServiceMap.cpp | 6 +++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index d7a4da27..340ea560 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -94,13 +94,12 @@ void BLEService::executeCreate(BLEServer *pServer) { /** * @brief Delete the service. * Delete the service. - * @param [in] gatts_if The handle of the GATT server interface. * @return N/A. */ void BLEService::executeDelete() { ESP_LOGD(LOG_TAG, ">> executeDelete()"); - m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); @@ -329,9 +328,18 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_START_EVT - case ESP_GATTS_STOP_EVT: - m_semaphoreStopEvt.give(); + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } break; + } // ESP_GATTS_STOP_EVT // ESP_GATTS_CREATE_EVT @@ -354,10 +362,20 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_CREATE_EVT + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // case ESP_GATTS_DELETE_EVT: { - m_semaphoreDeleteEvt.give(); + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } break; - } + } // ESP_GATTS_DELETE_EVT default: { break; diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index 78f00feb..dd828fae 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -120,9 +120,13 @@ BLEService* BLEServiceMap::getNext() { return pRet; } // getNext +/** + * @brief Removes service from maps. + * @return N/A. + */ void BLEServiceMap::removeService(BLEService *service){ m_handleMap.erase(service->getHandle()); m_uuidMap.erase(service); -} +} // removeService #endif /* CONFIG_BT_ENABLED */ From de9d41386b046bd260ee8e42189fde2e67995c63 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 21 Jun 2018 20:27:57 +0200 Subject: [PATCH 224/310] Add Arduino compatibility to BLEDevice::init --- cpp_utils/BLEDevice.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 1c6e6bb8..6aa7c6e3 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -331,14 +331,21 @@ uint16_t BLEDevice::m_localMTU = 23; if(!initialized){ initialized = true; // Set the initialization flag to ensure we are only initialized once. - esp_err_t errRc = ::nvs_flash_init(); + esp_err_t errRc = ESP_OK; +#ifdef ARDUINO_ARCH_ESP32 + if (!btStart()) { + errRc = ESP_FAIL; + return; + } +#else + errRc = ::nvs_flash_init(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - errRc = esp_bt_controller_init(&bt_cfg); + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + errRc = esp_bt_controller_init(&bt_cfg); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; @@ -358,18 +365,24 @@ uint16_t BLEDevice::m_localMTU = 23; ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } +#endif #endif - errRc = esp_bluedroid_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); + if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ + errRc = esp_bluedroid_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } } - errRc = esp_bluedroid_enable(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ + errRc = esp_bluedroid_enable(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } } errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); From 4743dfc22daa447f45b6416b4e872c0ac7bb6e6b Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 23 Jun 2018 11:34:30 -0500 Subject: [PATCH 225/310] Addition of missing header for Arduino --- cpp_utils/BLEDevice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 6aa7c6e3..a7db454b 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -30,6 +30,7 @@ #include "GeneralUtils.h" #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" +#include "esp32-hal-bt.h" #endif static const char* LOG_TAG = "BLEDevice"; From 7e1cf238767d5456235afba7c37830633968dc89 Mon Sep 17 00:00:00 2001 From: Damon Smith Date: Tue, 26 Jun 2018 22:33:59 +1000 Subject: [PATCH 226/310] added a way to connect to a Station while keeping the AP running --- cpp_utils/WiFi.cpp | 16 ++++++++++++---- cpp_utils/WiFi.h | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 503e9235..2372dea6 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -146,16 +146,24 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { /** - * @brief Connect to an external access point. + * @see WiFi::connectWithMode - this one defaults the mode to WIFI_MODE_AP + */ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ + return this->connectWithMode(ssid, password, waitForConnection, WIFI_MODE_AP); +} + +/** + * @brief Connect to an external access point and specify the mode (WIFI_MODE_AP or WIFI_MODE_APSTA). * * The event handler will be called back with the outcome of the connection. * * @param [in] ssid The network SSID of the access point to which we wish to connect. * @param [in] password The password of the access point to which we wish to connect. * @param [in] waitForConnection Block until the connection has an outcome. - * @returns ESP_OK if successfully receives a SYSTEM_EVENT_STA_GOT_IP event. Otherwise returns wifi_err_reason_t - use GeneralUtils::wifiErrorToString(uint8_t errCode) to print the error. + * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect + * @return N/A. */ -uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ ESP_LOGD(LOG_TAG, ">> connectAP"); m_apConnectionStatus = UINT8_MAX; @@ -172,7 +180,7 @@ uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bo ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); } - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + esp_err_t errRc = ::esp_wifi_set_mode(mode); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index a57360ca..09f3d56d 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -131,6 +131,7 @@ class WiFi { struct in_addr getHostByName(const std::string& hostName); struct in_addr getHostByName(const char* hostName); uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); + uint8_t connectWithMode(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode); void dump(); bool isConnectedToAP(); static std::string getApMac(); From 18a03f9a20d55bfc55648f43964222725cab3e7e Mon Sep 17 00:00:00 2001 From: Damon Smith Date: Tue, 26 Jun 2018 22:42:01 +1000 Subject: [PATCH 227/310] fixed to just use default value params --- cpp_utils/WiFi.cpp | 7 ------- cpp_utils/WiFi.h | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 2372dea6..f1a07217 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -145,13 +145,6 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { } // setDNSServer -/** - * @see WiFi::connectWithMode - this one defaults the mode to WIFI_MODE_AP - */ -uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ - return this->connectWithMode(ssid, password, waitForConnection, WIFI_MODE_AP); -} - /** * @brief Connect to an external access point and specify the mode (WIFI_MODE_AP or WIFI_MODE_APSTA). * diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index 09f3d56d..c1b6bedc 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -130,8 +130,7 @@ class WiFi { void setDNSServer(int numdns, ip_addr_t ip); struct in_addr getHostByName(const std::string& hostName); struct in_addr getHostByName(const char* hostName); - uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); - uint8_t connectWithMode(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); void dump(); bool isConnectedToAP(); static std::string getApMac(); From cc56443c45344fa48101403fb62cc1249c2e1f01 Mon Sep 17 00:00:00 2001 From: Damon Smith Date: Tue, 26 Jun 2018 22:43:51 +1000 Subject: [PATCH 228/310] fix up comments on connectAP --- cpp_utils/WiFi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index f1a07217..09d59d55 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -146,7 +146,7 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { /** - * @brief Connect to an external access point and specify the mode (WIFI_MODE_AP or WIFI_MODE_APSTA). + * @brief Connect to an external access point. * * The event handler will be called back with the outcome of the connection. * @@ -154,7 +154,7 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @param [in] password The password of the access point to which we wish to connect. * @param [in] waitForConnection Block until the connection has an outcome. * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect - * @return N/A. + * @return ESP_OK if we are now connected and wifi_err_reason_t if not. */ uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ ESP_LOGD(LOG_TAG, ">> connectAP"); From 8dbdaa504148f2207fad4e6e7b9974886f894685 Mon Sep 17 00:00:00 2001 From: Friedemann Date: Tue, 26 Jun 2018 15:36:05 +0200 Subject: [PATCH 229/310] added getServiceByUUID() to BLEServer --- cpp_utils/BLEServer.cpp | 18 ++++++++++++++++++ cpp_utils/BLEServer.h | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index d5c9de68..164d1c46 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -96,6 +96,24 @@ BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t } // createService +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(const char* uuid) { + return m_serviceMap.getByUUID(uuid); +} + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { + return m_serviceMap.getByUUID(uuid); +} + /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. * diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 95c55d53..8c140c44 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -63,7 +63,8 @@ class BLEServer { void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); void removeService(BLEService *service); - + BLEService* getServiceByUUID(const char* uuid); + BLEService* getServiceByUUID(BLEUUID uuid); private: BLEServer(); From d6ef14932ecd95b34fa9d066e179e10365ebb7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Cabral?= Date: Thu, 5 Jul 2018 10:32:04 +0100 Subject: [PATCH 230/310] FIX: BLESecurity.h -> BLESecurityCallbacks. Make all the methods pure virtual. Some of the methods were virtual, but without local definitions, this creates an error during linking. --- cpp_utils/BLESecurity.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index 67c41efb..2d52b015 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -51,20 +51,20 @@ class BLESecurityCallbacks { * It requires that our device is capable to display this code to end user * @param */ - virtual void onPassKeyNotify(uint32_t pass_key); + virtual void onPassKeyNotify(uint32_t pass_key) = 0; /** * @brief Here we can make decision if we want to let negotiate authorization with peer device or not * return Return true if we accept this peer device request */ - virtual bool onSecurityRequest(); + virtual bool onSecurityRequest() = 0 ; /** * Provide us information when authentication process is completed */ - virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t); + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; - virtual bool onConfirmPIN(uint32_t pin); + virtual bool onConfirmPIN(uint32_t pin) = 0; }; // BLESecurityCallbacks #endif // CONFIG_BT_ENABLED From 74b056fdb4256614c0e548bb2954131b22b97f32 Mon Sep 17 00:00:00 2001 From: Robert Klep Date: Fri, 6 Jul 2018 12:24:37 +0200 Subject: [PATCH 231/310] Handle `response` argument for BLERemoteDescriptor#writeValue() --- cpp_utils/BLERemoteDescriptor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 4d14ba80..2cdc7db1 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -148,7 +148,7 @@ void BLERemoteDescriptor::writeValue( getHandle(), length, // Data length data, // Data - ESP_GATT_WRITE_TYPE_NO_RSP, + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE ); if (errRc != ESP_OK) { From caa546abd4c0685191aa7b047449cbe99584626f Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 10 Jul 2018 18:12:42 +0200 Subject: [PATCH 232/310] Update remote characteristic to register for notify/indications (write to 0x2902) --- cpp_utils/BLERemoteCharacteristic.cpp | 12 +++++++++++- cpp_utils/BLERemoteCharacteristic.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index d9c64c91..907e0251 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -475,7 +475,7 @@ void BLERemoteCharacteristic::registerForNotify( BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, - bool isNotify)) { + bool isNotify), bool notifications) { ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); m_notifyCallback = notifyCallback; // Save the notification callback. @@ -492,6 +492,12 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + + uint8_t val[] = {0x01, 0x00}; + if(!notifications) + val[0] = 0x02; + BLERemoteDescriptor *desc = getDescriptorByUUID("0x2902"); + desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( @@ -503,6 +509,10 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + + uint8_t val[] = {0x00, 0x00}; + BLERemoteDescriptor *desc = getDescriptorByUUID("0x2902"); + desc->writeValue(val, 2); } // End Unregister m_semaphoreRegForNotifyEvt.wait("registerForNotify"); diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 6f23f497..10a2ec4c 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -44,7 +44,7 @@ class BLERemoteCharacteristic { uint8_t readUInt8(void); uint16_t readUInt16(void); uint32_t readUInt32(void); - void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)); + void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify), bool notifications = true); void writeValue(uint8_t* data, size_t length, bool response = false); void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); From 49d6abababc0b00d75c78a100ec15cf23d5c13b3 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 10 Jul 2018 18:22:45 +0200 Subject: [PATCH 233/310] Add onConnect callback with parameter to allow retrieve client MAC --- cpp_utils/BLEServer.cpp | 1 + cpp_utils/BLEServer.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index d5c9de68..272c1f0a 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -202,6 +202,7 @@ void BLEServer::handleGATTServerEvent( m_connId = param->connect.conn_id; // Save the connection id. if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); + m_pServerCallbacks->onConnect(this, param); } m_connectedCount++; // Increment the number of connected devices count. break; diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 585fdf89..417c129f 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -64,6 +64,7 @@ class BLEServer { void startAdvertising(); void removeService(BLEService *service); + private: BLEServer(); friend class BLEService; @@ -103,7 +104,7 @@ class BLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the client connection. */ virtual void onConnect(BLEServer* pServer); - + virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); /** * @brief Handle an existing client disconnection. * From 1012efc4e658c490ee7052fad9cdddff370e372b Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 10 Jul 2018 18:41:53 +0200 Subject: [PATCH 234/310] Add read raw data from remote characteristic --- cpp_utils/BLERemoteCharacteristic.cpp | 13 +++++++++++++ cpp_utils/BLERemoteCharacteristic.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 907e0251..1830c0cf 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -204,6 +204,11 @@ void BLERemoteCharacteristic::gattClientEventHandler( // and unlock the semaphore to ensure that the requestor of the data can continue. if (evtParam->read.status == ESP_GATT_OK) { m_value = std::string((char*)evtParam->read.value, evtParam->read.value_len); + if(m_rawData != nullptr) + free(m_rawData); + + m_rawData = calloc(evtParam->read.value_len, sizeof(uint8_t)); + memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); } else { m_value = ""; } @@ -613,4 +618,12 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp writeValue(std::string((char *)data, length), response); } // writeValue +/** + * @brief Read raw data from remote characteristic as hex bytes + * @return return pointer data read + */ +uint8_t* BLERemoteCharacteristic::readRawData() { + return m_rawData; +} + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 10a2ec4c..044f8f62 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -49,6 +49,7 @@ class BLERemoteCharacteristic { void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); std::string toString(void); + uint8_t* readRawData(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); @@ -76,6 +77,7 @@ class BLERemoteCharacteristic { FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; + uint8_t *m_rawData; void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. From 8cf68913ef4e4d82106544ed7f333c1b1fe9cbb4 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 10 Jul 2018 23:45:06 +0200 Subject: [PATCH 235/310] Quick fix --- cpp_utils/BLERemoteCharacteristic.cpp | 6 +++--- cpp_utils/BLEServer.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 1830c0cf..33d43eee 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -207,7 +207,7 @@ void BLERemoteCharacteristic::gattClientEventHandler( if(m_rawData != nullptr) free(m_rawData); - m_rawData = calloc(evtParam->read.value_len, sizeof(uint8_t)); + m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); } else { m_value = ""; @@ -501,7 +501,7 @@ void BLERemoteCharacteristic::registerForNotify( uint8_t val[] = {0x01, 0x00}; if(!notifications) val[0] = 0x02; - BLERemoteDescriptor *desc = getDescriptorByUUID("0x2902"); + BLERemoteDescriptor *desc = getDescriptor(BLEUUID("0x2902")); desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. @@ -516,7 +516,7 @@ void BLERemoteCharacteristic::registerForNotify( } uint8_t val[] = {0x00, 0x00}; - BLERemoteDescriptor *desc = getDescriptorByUUID("0x2902"); + BLERemoteDescriptor *desc = getDescriptor(BLEUUID("0x2902")); desc->writeValue(val, 2); } // End Unregister diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 272c1f0a..a124abcb 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -352,6 +352,12 @@ void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect +void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); From db780952f1d0f43cc30c28bb19e1bbc2f8f19b4f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 5 Aug 2018 19:08:29 -0500 Subject: [PATCH 236/310] Sync --- cpp_utils/AWS.cpp | 123 +++++++++ cpp_utils/AWS.h | 1 + cpp_utils/Console.cpp | 200 ++++++++++++++ cpp_utils/Console.h | 149 +++++++++++ cpp_utils/GeneralUtils.cpp | 21 +- cpp_utils/MMU.cpp | 127 +++++++++ cpp_utils/MMU.h | 22 ++ cpp_utils/System.cpp | 433 +++++++++++++++++++++++++++++++ cpp_utils/System.h | 2 + cpp_utils/Task.cpp | 2 +- cpp_utils/Task.h | 2 +- tasks/watchdogs/README.md | 6 + tools/bootloaderExamine/main.cpp | 6 + 13 files changed, 1082 insertions(+), 12 deletions(-) create mode 100644 cpp_utils/Console.cpp create mode 100644 cpp_utils/Console.h create mode 100644 cpp_utils/MMU.cpp create mode 100644 cpp_utils/MMU.h create mode 100644 tasks/watchdogs/README.md diff --git a/cpp_utils/AWS.cpp b/cpp_utils/AWS.cpp index 561162c3..7687be21 100644 --- a/cpp_utils/AWS.cpp +++ b/cpp_utils/AWS.cpp @@ -19,6 +19,129 @@ AWS::AWS() { AWS::~AWS() { } +/** + * Convert an AWS IoT error code to a string representation. + * @param err The error code to be mapped. + * @return A string representation of the error code. + */ +/* static */ std::string AWS::errorToString(IoT_Error_t err) { + switch(err) { + case NETWORK_PHYSICAL_LAYER_CONNECTED : + return "NETWORK_PHYSICAL_LAYER_CONNECTED"; + case NETWORK_MANUALLY_DISCONNECTED : + return "NETWORK_MANUALLY_DISCONNECTED"; + case NETWORK_ATTEMPTING_RECONNECT: + return "NETWORK_ATTEMPTING_RECONNECT"; + case NETWORK_RECONNECTED: + return "NETWORK_RECONNECTED"; + case MQTT_NOTHING_TO_READ : + return "MQTT_NOTHING_TO_READ"; + case MQTT_CONNACK_CONNECTION_ACCEPTED: + return "MQTT_CONNACK_CONNECTION_ACCEPTED"; + case SUCCESS : + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + case NULL_VALUE_ERROR : + return "NULL_VALUE_ERROR"; + case TCP_CONNECTION_ERROR : + return "TCP_CONNECTION_ERROR"; + case SSL_CONNECTION_ERROR: + return "SSL_CONNECTION_ERROR"; + case TCP_SETUP_ERROR : + return "TCP_SETUP_ERROR"; + case NETWORK_SSL_CONNECT_TIMEOUT_ERROR : + return "NETWORK_SSL_CONNECT_TIMEOUT_ERROR"; + case NETWORK_SSL_WRITE_ERROR : + return "NETWORK_SSL_WRITE_ERROR"; + case NETWORK_SSL_INIT_ERROR : + return "NETWORK_SSL_INIT_ERROR"; + case NETWORK_SSL_CERT_ERROR : + return "NETWORK_SSL_CERT_ERROR"; + case NETWORK_SSL_WRITE_TIMEOUT_ERROR : + return "NETWORK_SSL_WRITE_TIMEOUT_ERROR"; + case NETWORK_SSL_READ_TIMEOUT_ERROR : + return "NETWORK_SSL_READ_TIMEOUT_ERROR"; + case NETWORK_SSL_READ_ERROR : + return "NETWORK_SSL_READ_ERROR"; + case NETWORK_DISCONNECTED_ERROR : + return "NETWORK_DISCONNECTED_ERROR"; + case NETWORK_RECONNECT_TIMED_OUT_ERROR: + return "NETWORK_RECONNECT_TIMED_OUT_ERROR"; + case NETWORK_ALREADY_CONNECTED_ERROR : + return "NETWORK_ALREADY_CONNECTED_ERROR"; + case NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED : + return "NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED"; + case NETWORK_SSL_UNKNOWN_ERROR : + return "NETWORK_SSL_UNKNOWN_ERROR"; + case NETWORK_PHYSICAL_LAYER_DISCONNECTED : + return "NETWORK_PHYSICAL_LAYER_DISCONNECTED"; + case NETWORK_X509_ROOT_CRT_PARSE_ERROR : + return "NETWORK_X509_ROOT_CRT_PARSE_ERROR"; + case NETWORK_X509_DEVICE_CRT_PARSE_ERROR : + return "NETWORK_X509_DEVICE_CRT_PARSE_ERROR"; + case NETWORK_PK_PRIVATE_KEY_PARSE_ERROR : + return "NETWORK_PK_PRIVATE_KEY_PARSE_ERROR"; + case NETWORK_ERR_NET_SOCKET_FAILED : + return "NETWORK_ERR_NET_SOCKET_FAILED"; + case NETWORK_ERR_NET_UNKNOWN_HOST : + return "NETWORK_ERR_NET_UNKNOWN_HOST"; + case NETWORK_ERR_NET_CONNECT_FAILED : + return "NETWORK_ERR_NET_CONNECT_FAILED"; + case NETWORK_SSL_NOTHING_TO_READ : + return "NETWORK_SSL_NOTHING_TO_READ"; + case MQTT_CONNECTION_ERROR : + return "MQTT_CONNECTION_ERROR"; + case MQTT_CONNECT_TIMEOUT_ERROR : + return "MQTT_CONNECT_TIMEOUT_ERROR"; + case MQTT_REQUEST_TIMEOUT_ERROR: + return "MQTT_REQUEST_TIMEOUT_ERROR"; + case MQTT_UNEXPECTED_CLIENT_STATE_ERROR : + return "MQTT_UNEXPECTED_CLIENT_STATE_ERROR"; + case MQTT_CLIENT_NOT_IDLE_ERROR : + return "MQTT_CLIENT_NOT_IDLE_ERROR"; + case MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR : + return "MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR"; + case MQTT_RX_BUFFER_TOO_SHORT_ERROR : + return "MQTT_RX_BUFFER_TOO_SHORT_ERROR"; + case MQTT_TX_BUFFER_TOO_SHORT_ERROR : + return "MQTT_TX_BUFFER_TOO_SHORT_ERROR"; + case MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR : + return "MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR"; + case MQTT_DECODE_REMAINING_LENGTH_ERROR : + return "MQTT_DECODE_REMAINING_LENGTH_ERROR"; + case MQTT_CONNACK_UNKNOWN_ERROR : + return "MQTT_CONNACK_UNKNOWN_ERROR"; + case MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR : + return "MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR"; + case MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR: + return "MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR"; + case MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR : + return "MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR"; + case MQTT_CONNACK_BAD_USERDATA_ERROR: + return "MQTT_CONNACK_BAD_USERDATA_ERROR"; + case MQTT_CONNACK_NOT_AUTHORIZED_ERROR : + return "MQTT_CONNACK_NOT_AUTHORIZED_ERROR"; + case JSON_PARSE_ERROR : + return "JSON_PARSE_ERROR"; + case SHADOW_WAIT_FOR_PUBLISH : + return "SHADOW_WAIT_FOR_PUBLISH"; + case SHADOW_JSON_BUFFER_TRUNCATED : + return "SHADOW_JSON_BUFFER_TRUNCATED"; + case SHADOW_JSON_ERROR : + return "SHADOW_JSON_ERROR"; + case MUTEX_INIT_ERROR : + return "MUTEX_INIT_ERROR"; + case MUTEX_LOCK_ERROR: + return "MUTEX_LOCK_ERROR"; + case MUTEX_UNLOCK_ERROR : + return "MUTEX_UNLOCK_ERROR"; + case MUTEX_DESTROY_ERROR : + return "MUTEX_DESTROY_ERROR"; + default: + return "Unknown error!"; + } +} // AWS#errorToString /** * @brief Connect to the AWS IoT service. diff --git a/cpp_utils/AWS.h b/cpp_utils/AWS.h index 5d2c891c..abbd0613 100644 --- a/cpp_utils/AWS.h +++ b/cpp_utils/AWS.h @@ -24,6 +24,7 @@ class AWS { void connect(std::string clientId); void disconnect(); + static std::string errorToString(IoT_Error_t err); // Convert an AWS IoT error code to a string representation. void init(std::string host=CONFIG_AWS_IOT_MQTT_HOST, uint16_t port=CONFIG_AWS_IOT_MQTT_PORT); void publish(std::string topic, std::string payload, QoS qos = QOS0); void subscribe(std::string topic); diff --git a/cpp_utils/Console.cpp b/cpp_utils/Console.cpp new file mode 100644 index 00000000..c88d8372 --- /dev/null +++ b/cpp_utils/Console.cpp @@ -0,0 +1,200 @@ +/* + * Console.cpp + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +/** + * Example: + * Argtable argtable; + * argtable.addString("myparam", "l", "list", "Get Listings"); + * argtable.parse(argc, argv); + * ArgTableEntry_String* pStr = (ArgTableEntry_String*)argTable.get("myparam"); + * pStr->getValue(); + */ +#include "Console.h" + +/** + * Argtable instance constructor. + */ +ArgTable::ArgTable() { + m_argtable = nullptr; + m_argEnd = nullptr; +} // ArgTable#ArgTable + + +/** + * Argtable instance destructor. + */ +ArgTable::~ArgTable() { + freeArgtable(); // Release any resources associated with the argtable. +} // ArgTable#~ArgTable + + +/** + * Build the ArgTable that will be used for parsing. + */ +/* private */ void ArgTable::build() { + /* + * The m_argTableEntries is a std::list that contains the argtable entries in the form of a std::pair. We + * allocate storage for the ArgTable and then populate it. The last entry of the argtable must be an end marker. + */ + int size = m_argTableEntries.size(); + m_argtable = new void*[size + 1]; + int i=0; + for (auto it = m_argTableEntries.begin(); it != m_argTableEntries.end(); ++it) { + m_argtable[i] = it->second->getEntry(); + i++; + } + m_argEnd = arg_end(10); + m_argtable[i] = m_argEnd; +} // ArgTable#build + + +ArgTableEntry_Date ArgTable::addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Date* pDate = new ArgTableEntry_Date(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDate)); + return *pDate; +} // ArgTable#addDate + + +ArgTableEntry_Double ArgTable::addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Double* pDouble = new ArgTableEntry_Double(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDouble)); + return *pDouble; +} // ArgTable#addDouble + + +ArgTableEntry_File ArgTable::addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_File* pFile = new ArgTableEntry_File(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pFile)); + return *pFile; +} // ArgTable#addFile + + +ArgTableEntry_Int ArgTable::addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Int* pInt = new ArgTableEntry_Int(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pInt)); + return *pInt; +} // ArgTable#addInt + + +ArgTableEntry_Lit ArgTable::addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Lit* pLit = new ArgTableEntry_Lit(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pLit)); + return *pLit; +} // ArgTable#addLit + + +ArgTableEntry_String ArgTable::addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + ArgTableEntry_String* pStr = new ArgTableEntry_String(shortopts, longopts, glossary, min, max); + m_argTableEntries.push_back(std::make_pair(name, pStr)); + return *pStr; +} // ArgTable#addString + + +/** + * Parse the input and output parameters against this argtable. + * @param argc A count of the number of parameters. + * @param argv An array of string parameters. + * @return The number of errors detected. + */ +int ArgTable::parse(int argc, char* argv[]) { + if (m_argtable == nullptr) { // If we don't have an argtable, build it. + build(); + } + int nErrors = arg_parse(argc, argv, m_argtable); + return nErrors; +} // ArgTable#parse + + +/** + * Print any errors associated with the parsing. + */ +void ArgTable::printErrors(FILE* fp, std::string progName) { + if (m_argEnd != nullptr) { + arg_print_errors(fp, m_argEnd, progName.c_str()); + } +} // ArgTable#printErrors + + +/** + * Release the argtable data. + */ +/* private */void ArgTable::freeArgtable() { + if (m_argtable != nullptr) { + arg_free(m_argtable); + m_argtable = nullptr; + m_argEnd = nullptr; + } +} // ArgTable#freeArgtable + + +ArgTableEntry_Date::ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary) { + m_argDate = arg_daten(shortopts.c_str(), longopts.c_str(), "", "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DATE; +} // ArgTableEntry_Date#ArgTableEntry_Date + + +int ArgTableEntry_Date::getCount() { + return m_argDate->count; +} // ArgTableEntry_Date#getCount + + +ArgTableEntry_Double::ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary) { + m_argDbl = arg_dbln(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DBL; +} + +int ArgTableEntry_Double::getCount() { + return m_argDbl->count; +} + +ArgTableEntry_File::ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary) { + m_argFile = arg_filen(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::FILE; +} + +int ArgTableEntry_File::getCount() { + return m_argFile->count; +} + +ArgTableEntry_Int::ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary) { + m_argInt = arg_intn(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::INT; +} + +int ArgTableEntry_Int::getCount() { + return m_argInt->count; +} + +ArgTableEntry_Lit::ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary) { + m_argLit = arg_litn(shortopts.c_str(), longopts.c_str(), 0, 1, glossary.c_str()); + m_type = ArgType_t::LIT; +} + +int ArgTableEntry_Lit::getCount() { + return m_argLit->count; +} + +int ArgTableEntry_Regex::getCount() { + return m_argRex->count; +} + +ArgTableEntry_String::ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + m_argStr = arg_strn(shortopts.c_str(), longopts.c_str(), "", min, max, glossary.c_str()); + m_type = ArgType_t::STR; +} + +int ArgTableEntry_String::getCount() { + return m_argStr->count; +} + + +Console::Console() { +} + +Console::~Console() { +} + diff --git a/cpp_utils/Console.h b/cpp_utils/Console.h new file mode 100644 index 00000000..2ce40fa2 --- /dev/null +++ b/cpp_utils/Console.h @@ -0,0 +1,149 @@ +/* + * Console.h + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_CONSOLE_H_ +#define COMPONENTS_CPP_UTILS_CONSOLE_H_ + +#include +#include +#include +#include + +class Console { +public: + Console(); + virtual ~Console(); +}; + + +enum class ArgType_t { LIT, INT, DBL, STR, REX, FILE, DATE }; + + +class ArgTableEntry_Generic { +protected: + ArgType_t m_type; +public: + virtual int getCount(); + bool hasValue() { + return getCount() > 0; + } + virtual void* getEntry() = 0; +}; + + +class ArgTableEntry_Lit : public ArgTableEntry_Generic { +private: + struct arg_lit* m_argLit; +public: + int getCount(); + ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary); + void* getEntry() { + return m_argLit; + } +}; + +class ArgTableEntry_Int : public ArgTableEntry_Generic { +private: + struct arg_int* m_argInt; +public: + ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + int getValue(int index=0); + void* getEntry() { + return m_argInt; + } +}; + + +class ArgTableEntry_Double : public ArgTableEntry_Generic { +private: + struct arg_dbl* m_argDbl; +public: + ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + double getValue(int index=0); + void* getEntry() { + return m_argDbl; + } +}; + + +class ArgTableEntry_String : public ArgTableEntry_Generic { +private: + struct arg_str* m_argStr; +public: + ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int getCount(); + std::string getValue(int index=0); + void* getEntry() { + return m_argStr; + } +}; + + +class ArgTableEntry_Regex : public ArgTableEntry_Generic { +private: + struct arg_rex* m_argRex; +public: + int getCount(); + std::string getValue(int index=0); + void* getEntry() { + return m_argRex; + } +}; + + +class ArgTableEntry_File : public ArgTableEntry_Generic { +private: + struct arg_file* m_argFile; +public: + ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + std::string getFilename(int index=0); + std::string getBasename(int index=0); + std::string getExtension(int index=0); + void* getEntry() { + return m_argFile; + } +}; + + +class ArgTableEntry_Date : public ArgTableEntry_Generic { +private: + struct arg_date* m_argDate; +public: + ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + struct tm* getValue(int index=0); + void* getEntry() { + return m_argDate; + } +}; + + +class ArgTable { +private: + void** m_argtable; + struct arg_end* m_argEnd; + std::list> m_argTableEntries; + void build(); + void freeArgtable(); + +public: + ArgTable(); + ~ArgTable(); + ArgTableEntry_Date addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Double addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_File addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Int addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Lit addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_String addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int parse(int argc, char* argv[]); + void printErrors(FILE* fp, std::string progName=""); +}; + +#endif /* COMPONENTS_CPP_UTILS_CONSOLE_H_ */ diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 960f3172..e26d84c9 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -296,7 +296,8 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { char tempBuf[80]; uint32_t lineNumber = 0; - ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ----------------"); + ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + ESP_LOGD(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); strcpy(ascii, ""); strcpy(hex, ""); uint32_t index=0; @@ -365,23 +366,23 @@ std::vector GeneralUtils::split(std::string source, char delimiter) const char* GeneralUtils::errorToString(esp_err_t errCode) { switch(errCode) { case ESP_OK: - return "OK"; + return "ESP_OK"; case ESP_FAIL: - return "Fail"; + return "ESP_FAIL"; case ESP_ERR_NO_MEM: - return "No memory"; + return "ESP_ERR_NO_MEM"; case ESP_ERR_INVALID_ARG: - return "Invalid argument"; + return "ESP_ERR_INVALID_ARG"; case ESP_ERR_INVALID_SIZE: - return "Invalid state"; + return "ESP_ERR_INVALID_SIZE"; case ESP_ERR_INVALID_STATE: - return "Invalid state"; + return "ESP_ERR_INVALID_STATE"; case ESP_ERR_NOT_FOUND: - return "Not found"; + return "ESP_ERR_NOT_FOUND"; case ESP_ERR_NOT_SUPPORTED: - return "Not supported"; + return "ESP_ERR_NOT_SUPPORTED"; case ESP_ERR_TIMEOUT: - return "Timeout"; + return "ESP_ERR_TIMEOUT"; case ESP_ERR_NVS_NOT_INITIALIZED: return "ESP_ERR_NVS_NOT_INITIALIZED"; case ESP_ERR_NVS_NOT_FOUND: diff --git a/cpp_utils/MMU.cpp b/cpp_utils/MMU.cpp new file mode 100644 index 00000000..e43328fb --- /dev/null +++ b/cpp_utils/MMU.cpp @@ -0,0 +1,127 @@ +/* + * MMU.cpp + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#include "MMU.h" +#include +#include +#include + +// The following functions are provided by spi_flash.h +extern "C" { + extern void spi_flash_disable_interrupts_caches_and_other_cpu(); + + // Enable cache, enable interrupts (to be added in future), resume scheduler + extern void spi_flash_enable_interrupts_caches_and_other_cpu(); +} + + +typedef struct { + uint32_t low; + uint32_t high; +} addressRange_t; + +static addressRange_t entryNumberToAddressRange(uint32_t entryNumber) { + addressRange_t ret; + if (entryNumber < 64) { + ret.low = 0x3F400000 + 64*1024*entryNumber; + ret.high = 0x3F400000 + 64*1024*(entryNumber+1)-1; + return ret; + } + ret.low = 0x40000000 + 64*1024*(entryNumber-64); + ret.high = 0x40000000 + 64*1024*(entryNumber+1-64)-1; + return ret; +} + + +static uint32_t flashPageToOffset(uint32_t page) { + return page*64*1024; +} + + + +/* static */ void MMU::dump() { + const uint32_t mappingInvalid = 1 << 8; + + printf("PRO CPU MMU\n"); + for (int i=0; i<256; i++) { + if (!(DPORT_PRO_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff)); + } + } + printf("\n"); + printf("APP CPU MMU\n"); + for (int i=0; i<256; i++) { + if (!(DPORT_APP_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_APP_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_APP_FLASH_MMU_TABLE[i] & 0xff)); + } + } +} // MMU#dumpMMU + +extern "C" { + static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size); +} + +static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size) { + printf(">> MMU::mapFlashToVMA: flash offset: 0x%x, VMA: 0x%x, size: %d\n", flashOffset, (uint32_t)vma, size); + uint32_t mmuEntryStart; // The MMU table entry to start mapping. + uint32_t mmuEntryEnd; // The MMU table entry to end mapping. + + if ((uint32_t)vma >= 0x40000000 && (uint32_t)vma < 0x40C00000) { + mmuEntryStart = (((uint32_t)vma - 0x40000000)/(64*1024)) + 64; + mmuEntryEnd = (((uint32_t)vma - 0x40000000 + size)/(64*1024)) + 64; + } + else if ((uint32_t)vma >= 0x3F400000 && (uint32_t)vma < 0x3F800000) { + mmuEntryStart = (((uint32_t)vma - 0x3F400000)/(64*1024)); + mmuEntryEnd = (((uint32_t)vma - 0x3F400000 + size)/(64*1024)); + } + else { + printf(" - Unable to map from flash to VMA."); + return; + } + + // At this point we have populated mmuEntryStart and mmuEntryEnd which are the MMU table entries. + uint32_t pFlashStart = flashOffset; + uint32_t pFlashEnd = flashOffset + size; + + printf(" - Mapping flash to VMA via MMU. MMU entries start: %d, end: %d, mapping flash 0x%x (flash page: %d) to 0x%x (flash page: %d)\n", + mmuEntryStart, mmuEntryEnd, pFlashStart, pFlashStart/(64*1024), pFlashEnd, pFlashEnd/(64*1024)); + + uint32_t flashRegion = pFlashStart / (64*1024); // Determine the 64K chunk of flash to be mapped (we map in units of 64K). + + spi_flash_disable_interrupts_caches_and_other_cpu(); + + // For each of the mapping entries, map it to the corresponding flash region. + for (uint32_t i=mmuEntryStart; i<=mmuEntryEnd; i++) { + DPORT_PRO_FLASH_MMU_TABLE[i] = flashRegion; // There are two tables. One for the PRO CPU and one for the APP CPU. + DPORT_APP_FLASH_MMU_TABLE[i] = flashRegion; // Map both of them to the flash region. + flashRegion++; + } + + Cache_Flush(0); + Cache_Flush(1); + spi_flash_enable_interrupts_caches_and_other_cpu(); +} // mapFlashToVMA + +/** + * Map an area of flash memory into VMA. + * @param flashOffset The offset in flash of the start of data. + * @param vma Where in VMA the data should appear. + * @size How much data to map. + */ +void IRAM_ATTR MMU::mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size) { + mapFlashToVMA_Internal(flashOffset, vma, size); +} diff --git a/cpp_utils/MMU.h b/cpp_utils/MMU.h new file mode 100644 index 00000000..d8ba6b64 --- /dev/null +++ b/cpp_utils/MMU.h @@ -0,0 +1,22 @@ +/* + * MMU.h + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_MMU_H_ +#define COMPONENTS_CPP_UTILS_MMU_H_ +#include +#include +#include + +class MMU { +public: + MMU(); + virtual ~MMU(); + static void dump(); + static void mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size); +}; + +#endif /* COMPONENTS_CPP_UTILS_MMU_H_ */ diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index 1993be4c..e9e7c525 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -7,11 +7,133 @@ #include "System.h" #include +#include +#include extern "C" { #include } +typedef volatile struct { + union { + struct { + uint32_t mcu_oe: 1; + uint32_t slp_sel: 1; + uint32_t mcu_wpd: 1; + uint32_t mcu_wpu: 1; + uint32_t mcu_ie: 1; + uint32_t mcu_drb: 2; + uint32_t func_wpd: 1; + uint32_t func_wpu: 1; + uint32_t func_ie: 1; + uint32_t func_drv: 2; + uint32_t mcu_sel: 3; + uint32_t reserved15: 17; + }; + uint32_t val; + }; +} io_mux_reg_t; + + +typedef volatile struct { + union { + struct { + uint32_t clk1: 4; + uint32_t clk2: 4; + uint32_t clk3: 4; + uint32_t reserved12: 20; + }; + uint32_t val; + } pin_ctrl; + + // The 36 exposed pads. + io_mux_reg_t pad_gpio36; // GPIO36 + io_mux_reg_t pad_gpio37; // GPIO37 + io_mux_reg_t pad_gpio38; // GPIO38 + io_mux_reg_t pad_gpio39; // GPIO39 + io_mux_reg_t pad_gpio34; // GPIO34 + io_mux_reg_t pad_gpio35; // GPIO35 + io_mux_reg_t pad_gpio32; // GPIO32 + io_mux_reg_t pad_gpio33; // GPIO33 + io_mux_reg_t pad_gpio25; // GPIO25 + io_mux_reg_t pad_gpio26; // GPIO26 + io_mux_reg_t pad_gpio27; // GPIO27 + io_mux_reg_t pad_mtms; // GPIO14 + io_mux_reg_t pad_mtdi; // GPIO12 + io_mux_reg_t pad_mtck; // GPIO13 + io_mux_reg_t pad_mtdo; // GPIO15 + io_mux_reg_t pad_gpio2; // GPIO2 + io_mux_reg_t pad_gpio0; // GPIO0 + io_mux_reg_t pad_gpio4; // GPIO4 + io_mux_reg_t pad_gpio16; // GPIO16 + io_mux_reg_t pad_gpio17; // GPIO17 + io_mux_reg_t pad_sd_data2; // GPIO9 + io_mux_reg_t pad_sd_data3; // GPIO10 + io_mux_reg_t pad_sd_cmd; // GPIO11 + io_mux_reg_t pad_sd_clk; // GPIO6 + io_mux_reg_t pad_sd_data0; // GPIO7 + io_mux_reg_t pad_sd_data1; // GPIO8 + io_mux_reg_t pad_gpio5; // GPIO5 + io_mux_reg_t pad_gpio18; // GPIO18 + io_mux_reg_t pad_gpio19; // GPIO19 + io_mux_reg_t pad_gpio20; // GPIO20 + io_mux_reg_t pad_gpio21; // GPIO21 + io_mux_reg_t pad_gpio22; // GPIO22 + io_mux_reg_t pad_u0rxd; // GPIO3 + io_mux_reg_t pad_u0txd; // GPIO1 + io_mux_reg_t pad_gpio23; // GPIO23 + io_mux_reg_t pad_gpio24; // GPIO24 +} io_mux_dev_t; + +static io_mux_dev_t* IO_MUX = (io_mux_dev_t*)0x3ff49000; + +static const io_mux_reg_t* io_mux_translate[] = { + &IO_MUX->pad_gpio0, // 0 + &IO_MUX->pad_u0txd, // 1 + &IO_MUX->pad_gpio2, // 2 + &IO_MUX->pad_u0rxd, // 3 + &IO_MUX->pad_gpio4, // 4 + &IO_MUX->pad_gpio5, // 5 + &IO_MUX->pad_sd_clk, // 6 + &IO_MUX->pad_sd_data0, // 7 + &IO_MUX->pad_sd_data1, // 8 + &IO_MUX->pad_sd_data2, // 9 + &IO_MUX->pad_sd_data3, // 10 + &IO_MUX->pad_sd_cmd, // 11 + &IO_MUX->pad_mtdi, // 12 + &IO_MUX->pad_mtck, // 13 + &IO_MUX->pad_mtms, // 14 + &IO_MUX->pad_mtdo, // 15 + &IO_MUX->pad_gpio16, // 16 + &IO_MUX->pad_gpio17, // 17 + &IO_MUX->pad_gpio18, // 18 + &IO_MUX->pad_gpio19, // 19 + &IO_MUX->pad_gpio20, // 20 + &IO_MUX->pad_gpio21, // 21 + &IO_MUX->pad_gpio22, // 22 + &IO_MUX->pad_gpio23, // 23 + &IO_MUX->pad_gpio24, // 24 + &IO_MUX->pad_gpio25, // 25 + &IO_MUX->pad_gpio26, // 26 + &IO_MUX->pad_gpio27, // 27 + nullptr, // 28 + nullptr, // 29 + nullptr, // 30 + nullptr, // 31 + &IO_MUX->pad_gpio32, // 32 + &IO_MUX->pad_gpio33, // 33 + &IO_MUX->pad_gpio34, // 34 + &IO_MUX->pad_gpio35, // 35 + &IO_MUX->pad_gpio36, // 36 + &IO_MUX->pad_gpio37, // 37 + &IO_MUX->pad_gpio38, // 38 + &IO_MUX->pad_gpio39 // 39 +}; + +static const io_mux_reg_t* gpioToIoMux(int gpio) { + return io_mux_translate[gpio]; +} + System::System() { // TODO Auto-generated constructor stub @@ -21,6 +143,317 @@ System::~System() { // TODO Auto-generated destructor stub } +const static char* outSignalStrings[] = { + "SPICLK_out", // 0 + "SPIQ_out", // 1 + "SPID_out", // 2 + "SPIHD_out", //3 + "SPIWP_out", // 4 + "SPICS0_out", // 5 + "SPICS1_out", // 6 + "SPICS2_out", // 7 + "HSPICLK_out", // 8 + "HSPIQ_out", // 9 + "HSPID_out", // 10 + "HSPICS0_out", // 11 + "HSPIHD_out", // 12 + "HSPIWP_out", // 13 + "U0TXD_out", // 14 + "U0RTS_out", // 15 + "U0DTR_out", // 16 + "U1TXD_out", // 17 + "U1RTS_out", // 18 + "", // 19 + "", // 20 + "", // 21 + "", // 22 + "I2S0O_BCK_out", // 23 + "I2S1O_BCK_out", // 24 + "I2S0O_WS_out", // 25 + "I2S1O_WS_out", // 26 + "I2S0I_BCK_out", // 27 + "I2S0I_WS_out", // 28 + "I2CEXT0_SCL_out", // 29 + "I2CEXT0_SDA_out", // 30 + "sdio_tohost_int_out", // 31 + "pwm0_out0a", // 32 + "pwm0_out0b", // 33 + "pwm0_out1a", // 34 + "pwm0_out1b", // 35 + "pwm0_out2a", // 36 + "pwm0_out2b", //37 + "", // 38 + "", // 39 + "", // 40 + "", // 41 + "", // 42 + "", // 43 + "", // 44 + "", // 45 + "", // 46 + "", // 47 + "", // 48 + "", // 49 + "", // 50 + "", // 51 + "", // 52 + "", // 53 + "", // 54 + "", // 55 + "", // 56 + "", // 57 + "", // 58 + "", // 59 + "", // 60 + "HSPICS1_out", // 61 + "HSPICS2_out", // 62 + "VSPICLK_out_mux", // 63 + "VSPIQ_out", // 64 + "VSPID_out", // 65 + "VSPIHD_out", // 66 + "VSPIWP_out", // 67 + "VSPICS0_out", // 68 + "VSPICS1_out", // 69 + "VSPICS2_out", // 70 + "ledc_hs_sig_out0", // 71 + "ledc_hs_sig_out1", // 72 + "ledc_hs_sig_out2", // 73 + "ledc_hs_sig_out3", // 74 + "ledc_hs_sig_out4", // 75 + "ledc_hs_sig_out5", // 76 + "ledc_hs_sig_out6", // 77 + "ledc_hs_sig_out7", // 78 + "edc_ls_sig_out0", // 79 + "ledc_ls_sig_out1", // 80 + "ledc_ls_sig_out2", // 81 + "ledc_ls_sig_out3", // 82 + "ledc_ls_sig_out4", // 83 + "ledc_ls_sig_out5", // 84 + "ledc_ls_sig_out6", // 85 + "ledc_ls_sig_out7", // 86 + "rmt_sig_out0", // 87 + "rmt_sig_out1", // 88 + "rmt_sig_out2", // 89 + "rmt_sig_out3", // 90 + "rmt_sig_out4", // 91 + "rmt_sig_out5", // 92 + "rmt_sig_out6", // 93 + "rmt_sig_out7", // 94 + "I2CEXT1_SCL_out", // 95 + "I2CEXT1_SCL_out", // 96 + "host_ccmd_od_pullup_en_n", // 97 + "host_rst_n_1", // 98 + "host_rst_n_2", // 99 + "gpio_sd0_out", // 100 + "gpio_sd1_out", // 101 + "gpio_sd2_out", // 102 + "gpio_sd3_out", // 103 + "gpio_sd4_out", // 104 + "gpio_sd5_out", // 105 + "gpio_sd6_out", // 106 + "gpio_sd7_out", // 107 + "pwm1_out0a", // 108 + "pwm1_out0b", // 109 + "pwm1_out1a", // 110 + "pwm1_out1b", // 111 + "pwm1_out2a", // 112 + "pwm1_out2b", // 113 + "pwm2_out1h", // 114 + "pwm2_out1l", // 115 + "pwm2_out2h", // 116 + "pwm2_out2l", // 117 + "pwm2_out3h", // 118 + "pwm2_out3l", // 119 + "pwm2_out4h", // 120 + "pwm2_out4l", // 121 + "", // 122 + "", // 123 + "", // 124 + "", // 125 + "", // 126 + "", // 127 + "", // 128 + "", // 129 + "", // 130 + "", // 131 + "", // 132 + "", // 133 + "", // 134 + "", // 135 + "", // 136 + "", // 137 + "", // 138 + "", // 139 + "I2S0O_DATA_out0", // 140 + "I2S0O_DATA_out1", // 141 + "I2S0O_DATA_out2", // 142 + "I2S0O_DATA_out3", // 143 + "I2S0O_DATA_out4", // 144 + "I2S0O_DATA_out5", // 145 + "I2S0O_DATA_out6", // 146 + "I2S0O_DATA_out7", // 147 + "I2S0O_DATA_out8", // 148 + "I2S0O_DATA_out9", // 149 + "I2S0O_DATA_out10", // 150 + "I2S0O_DATA_out11", // 151 + "I2S0O_DATA_out12", // 152 + "I2S0O_DATA_out13", // 153 + "I2S0O_DATA_out14", // 154 + "I2S0O_DATA_out15", // 155 + "I2S0O_DATA_out16", // 156 + "I2S0O_DATA_out17", // 157 + "I2S0O_DATA_out18", // 158 + "I2S0O_DATA_out19", // 159 + "I2S0O_DATA_out20", // 160 + "I2S0O_DATA_out21", // 161 + "I2S0O_DATA_out22", // 162 + "I2S0O_DATA_out23", // 163 + "I2S1I_BCK_out", // 164 + "I2S1I_WS_out", // 165 + "I2S1O_DATA_out0", // 166 + "I2S1O_DATA_out1", // 167 + "I2S1O_DATA_out2", // 168 + "I2S1O_DATA_out3", // 169 + "I2S1O_DATA_out4", // 170 + "I2S1O_DATA_out5", // 171 + "I2S1O_DATA_out6", // 172 + "I2S1O_DATA_out7", // 173 + "I2S1O_DATA_out8", // 174 + "I2S1O_DATA_out9", // 175 + "I2S1O_DATA_out10", // 176 + "I2S1O_DATA_out11", // 177 + "I2S1O_DATA_out12", // 178 + "I2S1O_DATA_out13", // 179 + "I2S1O_DATA_out14", // 180 + "I2S1O_DATA_out15", // 181 + "I2S1O_DATA_out16", // 182 + "I2S1O_DATA_out17", // 183 + "I2S1O_DATA_out18", // 184 + "I2S1O_DATA_out19", // 185 + "I2S1O_DATA_out20", // 186 + "I2S1O_DATA_out21", // 187 + "I2S1O_DATA_out22", // 188 + "I2S1O_DATA_out23", // 189 + "pwm3_out1h", // 190 + "pwm3_out1l", // 191 + "pwm3_out2h", // 192 + "pwm3_out2l", // 193 + "pwm3_out3h", // 194 + "pwm3_out3l", // 195 + "pwm3_out4h", // 196 + "pwm3_out4l", // 197 + "U2TXD_out", // 198 + "U2RTS_out", // 199 + "emac_mdc_o", // 200 + "emac_mdo_o", // 201 + "emac_crs_o", // 202 + "emac_col_o", // 203 + "bt_audio0_irq", // 204 + "bt_audio1_irq", // 205 + "bt_audio2_irq", // 206 + "ble_audio0_irq", // 207 + "ble_audio1_irq", // 208 + "ble_audio2_irq", // 209 + "pcmfsync_out", // 210 + "pcmclk_out", // 211 + "pcmdout", // 212 + "ble_audio_sync0_p", // 213 + "ble_audio_sync1_p", // 214 + "ble_audio_sync2_p", // 215 + "", // 216 + "", // 217 + "", // 218 + "", // 219 + "", // 220 + "", // 221 + "", // 222 + "", // 223 + "sig_in_func224", // 224 + "sig_in_func225", // 225 + "sig_in_func226", // 226 + "sig_in_func227", // 227 + "sig_in_func228", // 228 + "", // 229 + "", // 230 + "", // 231 + "", // 232 + "", // 233 + "", // 234 + "", // 235 + "", // 236 + "", // 237 + "", // 238 + "", // 239 + "", // 240 + "", // 241 + "", // 242 + "", // 243 + "", // 244 + "", // 245 + "", // 246 + "", // 247 + "", // 248 + "", // 249 + "", // 250 + "", // 251 + "", // 252 + "", // 253 + "", // 254 + "", // 255 +}; + + +/** + * Dump the mappings for GPIO pins. + */ +/* static */ void System::dumpPinMapping() { + const int numPins = 40; + printf("GPIO_FUNCn_OUT_SEL_CFG_REG\n"); + printf("--------------------------\n"); + printf("%3s %4s\n", "Pin", "Func"); + for (int i=0; imcu_sel + 1, io_mux->func_ie); + } + } + +} // System#dumpPinMapping + + +/** + * Dump the storage stats for the heap. + */ +/* static */ void System::dumpHeapInfo() { + multi_heap_info_t heapInfo; + + printf(" %10s %10s %10s %10s %13s %11s %12s\n", "Free", "Allocated", "Largest", "Minimum", "Alloc Blocks", "Free Blocks", "Total Blocks"); + heap_caps_get_info(&heapInfo, MALLOC_CAP_EXEC); + printf("EXEC %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_32BIT); + printf("32BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_8BIT); + printf("8BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DMA); + printf("DMA %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_SPIRAM); + printf("SPISRAM %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_INTERNAL); + printf("INTERNAL %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DEFAULT); + printf("DEFAULT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); +} // System#dumpHeapInfo + + /** * @brief Get the information about the device. * @param [out] info The structure to be populated on return. diff --git a/cpp_utils/System.h b/cpp_utils/System.h index 6459509d..50337c32 100644 --- a/cpp_utils/System.h +++ b/cpp_utils/System.h @@ -18,6 +18,8 @@ class System { public: System(); virtual ~System(); + static void dumpPinMapping(); // Dump the mappings of pins to functions. + static void dumpHeapInfo(); static void getChipInfo(esp_chip_info_t *info); static size_t getFreeHeapSize(); static std::string getIDFVersion(); diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index a8ba9a37..2177b888 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -43,7 +43,7 @@ Task::~Task() { * @return N/A. */ -void Task::delay(int ms) { +/* static */ void Task::delay(int ms) { ::vTaskDelay(ms/portTICK_PERIOD_MS); } // delay diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 0d58f222..f7d88754 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -51,7 +51,7 @@ class Task { * @param [in] data The data passed in to the newly started task. */ virtual void run(void *data) = 0; // Make run pure virtual - void delay(int ms); + static void delay(int ms); private: xTaskHandle m_handle; diff --git a/tasks/watchdogs/README.md b/tasks/watchdogs/README.md new file mode 100644 index 00000000..85c7c5c9 --- /dev/null +++ b/tasks/watchdogs/README.md @@ -0,0 +1,6 @@ +#Watchdogs +This is a sample application that illustrates the capabilities of watchdogs and watchdog processing. + +A related YouTube video is available here: + +https://www.youtube.com/watch?v=C2xF3O6qkbg \ No newline at end of file diff --git a/tools/bootloaderExamine/main.cpp b/tools/bootloaderExamine/main.cpp index 22e8a5aa..60472e30 100644 --- a/tools/bootloaderExamine/main.cpp +++ b/tools/bootloaderExamine/main.cpp @@ -1,5 +1,6 @@ #include #include +#include /* Main header of binary image */ typedef struct { @@ -120,6 +121,11 @@ int main(int argc, char *argv[]) { return 0; } + if (header.magic != 0xE9) { + printf("Failed to find magic number (0xE9) in BIN file header.\n"); + return 0; + } + printf("Dump of ESP32 binary file: %s\n", fileName); printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s, hash_appended: %d\n", header.magic, header.segment_count, header.entry_addr, area(header.entry_addr), header.hash_appended); From f50bcb0731c783b0676efa30cfcefb5a7954895f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 5 Aug 2018 19:09:53 -0500 Subject: [PATCH 237/310] sync --- Documentation/ideas/FastFlash/README.md | 8 ++ tasks/watchdogs/README.md | 2 +- tasks/watchdogs/main.cpp | 155 ++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 Documentation/ideas/FastFlash/README.md create mode 100644 tasks/watchdogs/main.cpp diff --git a/Documentation/ideas/FastFlash/README.md b/Documentation/ideas/FastFlash/README.md new file mode 100644 index 00000000..d599b4aa --- /dev/null +++ b/Documentation/ideas/FastFlash/README.md @@ -0,0 +1,8 @@ +# FastFlash +As we build ESP32 applications, we typically perform a compile, flash, test loop cycle. We compile an app, we flash the ESP32 with that app and then we test whether it works. We perform these actions over and over again. When we look at the time taken in each step, we see there is compile time on our PC and then the time taken to flash the PC. This story talks about the time taken to flash the PC. + +The ESP32 is typically configured to flash at 115200 kbps. This is 115200 bits per second. If we think that a typical ESP32 application is 800KBytes then this requires a transmission of: + +800000 * 9 = 7.2 million bits = 62.5 seconds + +we can increase our baud rate up to 921600 = 7.8 seconds \ No newline at end of file diff --git a/tasks/watchdogs/README.md b/tasks/watchdogs/README.md index 85c7c5c9..052bc868 100644 --- a/tasks/watchdogs/README.md +++ b/tasks/watchdogs/README.md @@ -1,4 +1,4 @@ -#Watchdogs +# Watchdogs This is a sample application that illustrates the capabilities of watchdogs and watchdog processing. A related YouTube video is available here: diff --git a/tasks/watchdogs/main.cpp b/tasks/watchdogs/main.cpp new file mode 100644 index 00000000..de4484c5 --- /dev/null +++ b/tasks/watchdogs/main.cpp @@ -0,0 +1,155 @@ +#include "freertos/FreeRTOS.h" +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "nvs_flash.h" +#include "driver/gpio.h" +#include + +esp_err_t event_handler(void *ctx, system_event_t *event) +{ + return ESP_OK; +} + +extern "C" +{ + void app_main(void); +} + +void highPriorityTask(void *myData) +{ + printf("High priority task started and now looping for 10 seconds. Our priority is %d.\n", uxTaskPriorityGet(nullptr)); + TickType_t startTicks = xTaskGetTickCount(); + while (xTaskGetTickCount() - startTicks < (10 * 1000 / portTICK_PERIOD_MS)) + { + // Do nothing but loop + } + printf("High priority task ended\n"); + vTaskDelete(nullptr); +} + +void hardLoopTask(void *myData) +{ + printf("Hard loop task started ...\n"); + while (1) + { + // do nothing but burn CPU + } +} + +void hardLoopTaskNoInterrupts(void *myData) +{ + printf("Hard loop task disabling interrupts started ...\n"); + taskDISABLE_INTERRUPTS(); + while (1) + { + // do nothing but burn CPU + } +} + +void myTask(void *myData) +{ + printf("# Running in myTask\n"); + printf("# Registering our new task with the task watchdog.\n"); + esp_task_wdt_add(nullptr); + + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + printf("# Removing our watchdog registration so we can do something expensive.\n"); + esp_task_wdt_delete(nullptr); + + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Re-registering our task with the task watchdog.\n"); + esp_task_wdt_add(nullptr); + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Our current task priority is %d.\n", uxTaskPriorityGet(nullptr)); + printf("# Spwaning a higher priority task\n"); + xTaskCreate(highPriorityTask, // Task code + "Priority task", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 5, // Priority + nullptr // task handle + ); + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + printf("Spawning a hard-loop function!\n"); + xTaskCreate(hardLoopTaskNoInterrupts, // Task code + "Hard Loop", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 5, // Priority + nullptr // task handle + ); + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + + printf("# Removing our watchdog registration before we end the task.\n"); + esp_task_wdt_delete(nullptr); + + printf("# Ending myTask\n"); + vTaskDelete(nullptr); +} // myTask + +void app_main(void) +{ + xTaskHandle handle; + printf("App starting\n"); + printf("Initializing the task watchdog subsystem with an interval of 2 seconds.\n"); + esp_task_wdt_init(2, false); + + printf("Creatign a new task.\n"); + // Now let us create a new task. + xTaskCreate(myTask, // Task code + "My Task", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 0, // Priority + &handle // task handle + ); + + //printf("App Ended!\n"); +} // app_main From 5f0a1685aa803e58b94b1b398cb8452ae10e91b7 Mon Sep 17 00:00:00 2001 From: Friedemann Stoffregen Date: Tue, 7 Aug 2018 16:10:21 +0200 Subject: [PATCH 238/310] fixes #447 --- .DS_Store | Bin 0 -> 6148 bytes cpp_utils/{onhold => }/BLEEddystoneTLM.cpp | 84 ++++++++++++--------- cpp_utils/{onhold => }/BLEEddystoneTLM.h | 0 cpp_utils/{onhold => }/BLEEddystoneURL.cpp | 0 cpp_utils/{onhold => }/BLEEddystoneURL.h | 0 5 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 .DS_Store rename cpp_utils/{onhold => }/BLEEddystoneTLM.cpp (66%) mode change 100644 => 100755 rename cpp_utils/{onhold => }/BLEEddystoneTLM.h (100%) mode change 100644 => 100755 rename cpp_utils/{onhold => }/BLEEddystoneURL.cpp (100%) mode change 100644 => 100755 rename cpp_utils/{onhold => }/BLEEddystoneURL.h (100%) mode change 100644 => 100755 diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a1b1628574996553bb72515f29175d569efcf42f GIT binary patch literal 6148 zcmeHK&2G~`5S~p^u+>O4QV&Jquo4F@ImA>g;%0^BR;dU_^uSNBvDLV;?8tVKLlnt< zfj&U#725ZJhv3LdaA#(BgQ(J8s}{{zv)}IQ%>I0~cDw)}dXvZnZ~(wUC2ZJOTqD#^ zx*`o@Arxwi2~?0k3S~H3@n*waWPskC4WD6*&k+83f8{t#^ALM~hvPIUt8VwFXf#`E z4<1^!z23Il8|@GND4+RNP)*8SP`sno>!2EiK{0fm_(>FF?J$X|(IC$HX*`XygJM6Z zV(mJ}^AsIa*dPesCh=_OY`)5~DoC=pP-E*c-XA7el+SwkB#TPrZw9xlr{2*oTqEwm zvWL#re7^JIWk>F~i;kST-EFrccb~h9g>5~4w*BVg@yY4gm-DaRE-t^TAQ1T3DtTmZ z2|uuM4J}i@$kIGJ!ro=%GJ=sAU +#include #include #include "BLEEddystoneTLM.h" @@ -54,46 +54,62 @@ uint32_t BLEEddystoneTLM::getTime() { } // getTime std::string BLEEddystoneTLM::toString() { + std::stringstream ss; std::string out = ""; - String buff; uint32_t rawsec; + ss << "Version "; + ss << std::dec << m_eddystoneData.version; + ss << "\n"; - out += "Version "; - buff = String(m_eddystoneData.version, DEC); - out += buff.c_str(); - out += "\n"; + ss << "Battery Voltage "; + ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt); + ss << " mV\n"; - out += "Battery Voltage "; - buff = String(ENDIAN_CHANGE_U16(m_eddystoneData.volt), DEC); - out += buff.c_str(); - out += " mV\n"; + ss << "Temperature "; + ss << (float)m_eddystoneData.temp; + ss << " °C\n"; - out += "Temperature "; - buff = String((float)m_eddystoneData.temp, 1); - out += buff.c_str(); - out += " °C\n"; - - out += "Adv. Count "; - buff = String(ENDIAN_CHANGE_U32(m_eddystoneData.advCount), DEC); - out += buff.c_str(); - out += "\n"; + ss << "Adv. Count "; + ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount); + + ss << "\n"; - out += "Time "; + ss << "Time "; + rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); - buff = "0000"+String(rawsec/864000, DEC); - out += buff.substring(buff.length()-4,buff.length()).c_str(); - out += "."; - buff = "00"+String((rawsec/36000)%24, DEC); - out += buff.substring(buff.length()-2,buff.length()).c_str(); - out += ":"; - buff = "00"+String((rawsec/600)%60, DEC); - out += buff.substring(buff.length()-2,buff.length()).c_str(); - out += ":"; - buff = "00"+String((rawsec/10)%60, DEC); - out += buff.substring(buff.length()-2,buff.length()).c_str(); - out += "\n"; - - return out; + std::stringstream buffstream; + buffstream << "0000"; + buffstream << std::dec << rawsec/864000; + std::string buff = buffstream.str(); + + ss << buff.substr(buff.length()-4, buff.length()); + ss << "."; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec/36000)%24; + buff = buffstream.str(); + ss << buff.substr(buff.length()-2, buff.length()); + ss << ":"; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec/600)%60; + buff = buffstream.str(); + ss << buff.substr(buff.length()-2, buff.length()); + ss << ":"; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec/10)%60; + buff = buffstream.str(); + ss << buff.substr(buff.length()-2, buff.length()); + ss << "\n"; + + return ss.str(); } // toString /** diff --git a/cpp_utils/onhold/BLEEddystoneTLM.h b/cpp_utils/BLEEddystoneTLM.h old mode 100644 new mode 100755 similarity index 100% rename from cpp_utils/onhold/BLEEddystoneTLM.h rename to cpp_utils/BLEEddystoneTLM.h diff --git a/cpp_utils/onhold/BLEEddystoneURL.cpp b/cpp_utils/BLEEddystoneURL.cpp old mode 100644 new mode 100755 similarity index 100% rename from cpp_utils/onhold/BLEEddystoneURL.cpp rename to cpp_utils/BLEEddystoneURL.cpp diff --git a/cpp_utils/onhold/BLEEddystoneURL.h b/cpp_utils/BLEEddystoneURL.h old mode 100644 new mode 100755 similarity index 100% rename from cpp_utils/onhold/BLEEddystoneURL.h rename to cpp_utils/BLEEddystoneURL.h From b1922a86aff31da7ad2526fdcdfdd363ed0be511 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 16 Aug 2018 15:57:31 -0500 Subject: [PATCH 239/310] Initial samples for VSCode --- VisualStudioCode/README.md | 4 + VisualStudioCode/c_cpp_properties.json | 266 +++++++++++++++++++++++++ VisualStudioCode/launch.json | 43 ++++ VisualStudioCode/settings.json | 2 + VisualStudioCode/tasks.json | 41 ++++ eclipse/c_includes.xml | 19 +- 6 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 VisualStudioCode/README.md create mode 100644 VisualStudioCode/c_cpp_properties.json create mode 100644 VisualStudioCode/launch.json create mode 100644 VisualStudioCode/settings.json create mode 100644 VisualStudioCode/tasks.json diff --git a/VisualStudioCode/README.md b/VisualStudioCode/README.md new file mode 100644 index 00000000..ff5ab2f2 --- /dev/null +++ b/VisualStudioCode/README.md @@ -0,0 +1,4 @@ +These are file for Microsoft Visual Studio Code and can be copied into your `.vscode` project folder. For more information on this area, see: + +* [VSCode JTAG Debugging of ESP32 - Part 2](https://gojimmypi.blogspot.com/2017/05/vscode-remote-jtag-debugging-of-esp32.html) +* [Deous/VSC-Guide-for-esp32](https://github.com/Deous/VSC-Guide-for-esp32) \ No newline at end of file diff --git a/VisualStudioCode/c_cpp_properties.json b/VisualStudioCode/c_cpp_properties.json new file mode 100644 index 00000000..9ef83b14 --- /dev/null +++ b/VisualStudioCode/c_cpp_properties.json @@ -0,0 +1,266 @@ +{ + "configurations": [ + { + "name": "ESP32-Linux", + "includePath": [ + "${workspaceRoot}", + "${workspaceRoot}/components", + "${workspaceRoot}/build", + "${workspaceRoot}/build/include", + "${env:IDF_PATH}/components/bt/bluedroid/utils/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/smp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/sdp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/rfcomm/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/l2cap/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/gatt/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/gap/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avrc/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avdt/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avct/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/a2dp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/include", + "${env:IDF_PATH}/components/bt/bluedroid/osi/include", + "${env:IDF_PATH}/components/bt/bluedroid/hci/include", + "${env:IDF_PATH}/components/bt/bluedroid/gki/include", + "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/encoder/include", + "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/decoder/include", + "${env:IDF_PATH}/components/bt/bluedroid/device/include", + "${env:IDF_PATH}/components/bt/bluedroid/btcore/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/smp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/hid/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/dis/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/battery/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/a2dp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/blufi/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/include", + "${env:IDF_PATH}/components/bt/bluedroid/bta/sys/include", + "${env:IDF_PATH}/components/bt/bluedroid/bta/include", + "${env:IDF_PATH}/components/bt/bluedroid/api/include", + "${env:IDF_PATH}/components/bt/bluedroid/include", + "${env:IDF_PATH}/components/aws_iot/include", + "${env:IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include", + "${env:IDF_PATH}/components/app_trace/include", + "${env:IDF_PATH}/components/app_update/include", + "${env:IDF_PATH}/components/xtensa-debug-module/include", + "${env:IDF_PATH}/components/bootloader_support/include", + "${env:IDF_PATH}/components/bootloader_support/include_priv", + "${env:IDF_PATH}/components/bt/include", + "${env:IDF_PATH}/components/coap/port/include", + "${env:IDF_PATH}/components/coap/port/include/coap", + "${env:IDF_PATH}/components/coap/libcoap/include", + "${env:IDF_PATH}/components/coap/libcoap/include/coap", + "${env:IDF_PATH}/components/cxx/include", + "${env:IDF_PATH}/components/driver/include", + "${env:IDF_PATH}/components/driver/include/driver", + "${env:IDF_PATH}/components/esp32/include", + "${env:IDF_PATH}/components/ethernet/include", + "${env:IDF_PATH}/components/expat/include/expat", + "${env:IDF_PATH}/components/expat/port/include", + "${env:IDF_PATH}/components/fatfs/src", + "${env:IDF_PATH}/components/freertos/include", + "${env:IDF_PATH}/components/heap/include", + "${env:IDF_PATH}/components/jsmn/include", + "${env:IDF_PATH}/components/json/include", + "${env:IDF_PATH}/components/json/port/include", + "${env:IDF_PATH}/components/json/cJSON", + "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include", + "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include/sodium", + "${env:IDF_PATH}/components/log/include", + "${env:IDF_PATH}/components/lwip/include/lwip", + "${env:IDF_PATH}/components/lwip/include/lwip/port", + "${env:IDF_PATH}/components/lwip/include/lwip/posix", + "${env:IDF_PATH}/components/lwip/apps/ping", + "${env:IDF_PATH}/components/lwip/include/lwip/apps", + "${env:IDF_PATH}/components/lwip/include/lwip/apps/sntp", + "${env:IDF_PATH}/components/lwip/include/lwip/lwip", + "${env:IDF_PATH}/components/lwip/include/lwip/lwip/priv", + "${env:IDF_PATH}/components/lwip/include/lwip/netif", + "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp", + "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp/polarssl", + "${env:IDF_PATH}/components/lwip/include/lwip/port", + "${env:IDF_PATH}/components/lwip/include/lwip/port/arch", + "${env:IDF_PATH}/components/lwip/include/lwip/port/arpa", + "${env:IDF_PATH}/components/lwip/include/lwip/port/netif", + "${env:IDF_PATH}/components/lwip/include/lwip/port/netinet", + "${env:IDF_PATH}/components/lwip/include/lwip/posix", + "${env:IDF_PATH}/components/lwip/include/lwip/posix/sys", + "${env:IDF_PATH}/components/mbedtls/port/include", + "${env:IDF_PATH}/components/mbedtls/mbedtls/include", + "${env:IDF_PATH}/components/mbedtls/port/include/mbedtls", + "${env:IDF_PATH}/components/mdns/include", + "${env:IDF_PATH}/components/micro-ecc/micro-ecc", + "${env:IDF_PATH}/components/newlib/include", + "${env:IDF_PATH}/components/newlib/include/sys", + "${env:IDF_PATH}/components/newlib/platform_include", + "${env:IDF_PATH}/components/nghttp/include", + "${env:IDF_PATH}/components/nghttp/port/include", + "${env:IDF_PATH}/components/nvs_flash/include", + "${env:IDF_PATH}/components/openssl/include", + "${env:IDF_PATH}/components/openssl/include/internal", + "${env:IDF_PATH}/components/openssl/include/platform", + "${env:IDF_PATH}/components/openssl/include/openssl", + "${env:IDF_PATH}/components/pthread/include", + "${env:IDF_PATH}/components/sdmmc/include", + "${env:IDF_PATH}/components/spi_flash/include", + "${env:IDF_PATH}/components/tcpip_adapter/include", + "${env:IDF_PATH}/components/soc/esp32/include", + "${env:IDF_PATH}/components/soc/include", + "${env:IDF_PATH}/components/soc/esp32/include/soc", + "${env:IDF_PATH}/components/spi_flash", + "${env:IDF_PATH}/components/spiffs/include", + "${env:IDF_PATH}/components/tcpip_adapter/include", + "${env:IDF_PATH}/components/heap/include", + "${env:IDF_PATH}/components/ulp/include", + "${env:IDF_PATH}/components/ulp/include/esp32", + "${env:IDF_PATH}/components/vfs/include", + "${env:IDF_PATH}/components/vfs/include/sys", + "${env:IDF_PATH}/components/wear_levelling/include", + "${env:IDF_PATH}/components/wpa_supplicant/include", + "${env:IDF_PATH}/components/wpa_supplicant/port/include", + "${env:IDF_PATH}/components/wpa_supplicant/include/crypto", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/eap_peer", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/tls", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/utils", + "${env:IDF_PATH}/components/xtensa-debug-module/include", + "C:/Program Files/Espressif/ESP-IDF Tools/toolchain/lib/gcc/xtensa-esp32-elf/5.2.0/include" + ], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "${workspaceRoot}", + "${workspaceRoot}/components", + "${workspaceRoot}/build", + "${workspaceRoot}/build/include", + "${env:IDF_PATH}/components/bt/bluedroid/utils/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/smp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/sdp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/rfcomm/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/l2cap/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/gatt/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/gap/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avrc/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avdt/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/avct/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/a2dp/include", + "${env:IDF_PATH}/components/bt/bluedroid/stack/include", + "${env:IDF_PATH}/components/bt/bluedroid/osi/include", + "${env:IDF_PATH}/components/bt/bluedroid/hci/include", + "${env:IDF_PATH}/components/bt/bluedroid/gki/include", + "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/encoder/include", + "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/decoder/include", + "${env:IDF_PATH}/components/bt/bluedroid/device/include", + "${env:IDF_PATH}/components/bt/bluedroid/btcore/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/smp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/hid/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/dis/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/battery/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/a2dp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/blufi/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/include", + "${env:IDF_PATH}/components/bt/bluedroid/btc/include", + "${env:IDF_PATH}/components/bt/bluedroid/bta/sys/include", + "${env:IDF_PATH}/components/bt/bluedroid/bta/include", + "${env:IDF_PATH}/components/bt/bluedroid/api/include", + "${env:IDF_PATH}/components/bt/bluedroid/include", + "${env:IDF_PATH}/components/aws_iot/include", + "${env:IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include", + "${env:IDF_PATH}/components/app_trace/include", + "${env:IDF_PATH}/components/app_update/include", + "${env:IDF_PATH}/components/xtensa-debug-module/include", + "${env:IDF_PATH}/components/bootloader_support/include", + "${env:IDF_PATH}/components/bootloader_support/include_priv", + "${env:IDF_PATH}/components/bt/include", + "${env:IDF_PATH}/components/coap/port/include", + "${env:IDF_PATH}/components/coap/port/include/coap", + "${env:IDF_PATH}/components/coap/libcoap/include", + "${env:IDF_PATH}/components/coap/libcoap/include/coap", + "${env:IDF_PATH}/components/cxx/include", + "${env:IDF_PATH}/components/driver/include", + "${env:IDF_PATH}/components/driver/include/driver", + "${env:IDF_PATH}/components/esp32/include", + "${env:IDF_PATH}/components/ethernet/include", + "${env:IDF_PATH}/components/expat/include/expat", + "${env:IDF_PATH}/components/expat/port/include", + "${env:IDF_PATH}/components/fatfs/src", + "${env:IDF_PATH}/components/freertos/include", + "${env:IDF_PATH}/components/heap/include", + "${env:IDF_PATH}/components/jsmn/include", + "${env:IDF_PATH}/components/json/include", + "${env:IDF_PATH}/components/json/port/include", + "${env:IDF_PATH}/components/json/cJSON", + "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include", + "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include/sodium", + "${env:IDF_PATH}/components/log/include", + "${env:IDF_PATH}/components/lwip/include/lwip", + "${env:IDF_PATH}/components/lwip/include/lwip/port", + "${env:IDF_PATH}/components/lwip/include/lwip/posix", + "${env:IDF_PATH}/components/lwip/apps/ping", + "${env:IDF_PATH}/components/lwip/include/lwip/apps", + "${env:IDF_PATH}/components/lwip/include/lwip/apps/sntp", + "${env:IDF_PATH}/components/lwip/include/lwip/lwip", + "${env:IDF_PATH}/components/lwip/include/lwip/lwip/priv", + "${env:IDF_PATH}/components/lwip/include/lwip/netif", + "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp", + "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp/polarssl", + "${env:IDF_PATH}/components/lwip/include/lwip/port", + "${env:IDF_PATH}/components/lwip/include/lwip/port/arch", + "${env:IDF_PATH}/components/lwip/include/lwip/port/arpa", + "${env:IDF_PATH}/components/lwip/include/lwip/port/netif", + "${env:IDF_PATH}/components/lwip/include/lwip/port/netinet", + "${env:IDF_PATH}/components/lwip/include/lwip/posix", + "${env:IDF_PATH}/components/lwip/include/lwip/posix/sys", + "${env:IDF_PATH}/components/mbedtls/port/include", + "${env:IDF_PATH}/components/mbedtls/include", + "${env:IDF_PATH}/components/mbedtls/port/include/mbedtls", + "${env:IDF_PATH}/components/mdns/include", + "${env:IDF_PATH}/components/micro-ecc/micro-ecc", + "${env:IDF_PATH}/components/newlib/include", + "${env:IDF_PATH}/components/newlib/include/sys", + "${env:IDF_PATH}/components/newlib/platform_include", + "${env:IDF_PATH}/components/nghttp/include", + "${env:IDF_PATH}/components/nghttp/port/include", + "${env:IDF_PATH}/components/nvs_flash/include", + "${env:IDF_PATH}/components/openssl/include", + "${env:IDF_PATH}/components/openssl/include/internal", + "${env:IDF_PATH}/components/openssl/include/platform", + "${env:IDF_PATH}/components/openssl/include/openssl", + "${env:IDF_PATH}/components/pthread/include", + "${env:IDF_PATH}/components/sdmmc/include", + "${env:IDF_PATH}/components/spi_flash/include", + "${env:IDF_PATH}/components/tcpip_adapter/include", + "${env:IDF_PATH}/components/soc/esp32/include", + "${env:IDF_PATH}/components/soc/include", + "${env:IDF_PATH}/components/soc/esp32/include/soc", + "${env:IDF_PATH}/components/spi_flash", + "${env:IDF_PATH}/components/spiffs/include", + "${env:IDF_PATH}/components/tcpip_adapter/include", + "${env:IDF_PATH}/components/heap/include", + "${env:IDF_PATH}/components/ulp/include", + "${env:IDF_PATH}/components/ulp/include/esp32", + "${env:IDF_PATH}/components/vfs/include", + "${env:IDF_PATH}/components/vfs/include/sys", + "${env:IDF_PATH}/components/wear_levelling/include", + "${env:IDF_PATH}/components/wpa_supplicant/include", + "${env:IDF_PATH}/components/wpa_supplicant/port/include", + "${env:IDF_PATH}/components/wpa_supplicant/include/crypto", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/eap_peer", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/tls", + "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/utils", + "${env:IDF_PATH}/components/xtensa-debug-module/include", + "C:/Program Files/Espressif/ESP-IDF Tools/toolchain/lib/gcc/xtensa-esp32-elf/5.2.0/include" + + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db" + }, + "cStandard": "c11", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/VisualStudioCode/launch.json b/VisualStudioCode/launch.json new file mode 100644 index 00000000..257de4a0 --- /dev/null +++ b/VisualStudioCode/launch.json @@ -0,0 +1,43 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "ESP32 OpenOCD launch", + "type": "cppdbg", + "request": "launch", + "program": "./build/app-template.elf", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "text": "target remote localhost:3333" + }, + { + "text": "monitor reset halt" + }, + { + "text": "flushregs" + }, + { + "text": "thb app_main" + } + ], + "logging": { + "trace": true, + "traceResponse": true, + "engineLogging": true + } + } + ] +} \ No newline at end of file diff --git a/VisualStudioCode/settings.json b/VisualStudioCode/settings.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/VisualStudioCode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/VisualStudioCode/tasks.json b/VisualStudioCode/tasks.json new file mode 100644 index 00000000..d989b0f2 --- /dev/null +++ b/VisualStudioCode/tasks.json @@ -0,0 +1,41 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "menuconfig", + "type": "shell", + "command": "make menuconfig", + "problemMatcher": [] + }, + { + "label": "make", + "type": "shell", + "command": "make -j5", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "flash", + "type": "shell", + "command": "make flash monitor", + "problemMatcher": [] + }, + { + "label": "monitor", + "type": "shell", + "command": "make monitor", + "problemMatcher": [] + }, + { + "label": "clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/eclipse/c_includes.xml b/eclipse/c_includes.xml index eaa53ba2..60400ad5 100644 --- a/eclipse/c_includes.xml +++ b/eclipse/c_includes.xml @@ -50,7 +50,6 @@ ${IDF_PATH}/components/spi_flash/include ${IDF_PATH}/components/mbedtls/include ${IDF_PATH}/components/mdns/include -${IDF_PATH}/components/json/include ${IDF_PATH}/components/bt/include ${IDF_PATH}/components/bt/bluedroid/bta/include ${IDF_PATH}/components/bt/bluedroid/bta/sys/include @@ -128,18 +127,36 @@ ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + From ed1de4b54a585e126649e19a19bc39566fb50a88 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sat, 25 Aug 2018 19:21:50 -0500 Subject: [PATCH 240/310] Addition of GCP JWT sample --- cloud/GCP/JWT/base64url.cpp | 136 ++++++++++++++++++++++++++ cloud/GCP/JWT/base64url.h | 15 +++ cloud/GCP/JWT/main.cpp | 189 ++++++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+) create mode 100644 cloud/GCP/JWT/base64url.cpp create mode 100644 cloud/GCP/JWT/base64url.h create mode 100644 cloud/GCP/JWT/main.cpp diff --git a/cloud/GCP/JWT/base64url.cpp b/cloud/GCP/JWT/base64url.cpp new file mode 100644 index 00000000..94221181 --- /dev/null +++ b/cloud/GCP/JWT/base64url.cpp @@ -0,0 +1,136 @@ +// https://raw.githubusercontent.com/zhicheng/base64/master/base64.c +/* This is a public domain base64 implementation written by WEI Zhicheng. */ + +#include + +#include "base64url.h" + +/* BASE 64 encode table */ +static const char base64en[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', +}; + +#define BASE64_PAD '=' + + +#define BASE64DE_FIRST '+' +#define BASE64DE_LAST 'z' +/* ASCII order for BASE 64 decode, -1 in unused character */ +static const signed char base64de[] = { + /* '+', ',', '-', '.', '/', '0', '1', '2', */ + 62, -1, -1, -1, 63, 52, 53, 54, + + /* '3', '4', '5', '6', '7', '8', '9', ':', */ + 55, 56, 57, 58, 59, 60, 61, -1, + + /* ';', '<', '=', '>', '?', '@', 'A', 'B', */ + -1, -1, -1, -1, -1, -1, 0, 1, + + /* 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', */ + 2, 3, 4, 5, 6, 7, 8, 9, + + /* 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', */ + 10, 11, 12, 13, 14, 15, 16, 17, + + /* 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', */ + 18, 19, 20, 21, 22, 23, 24, 25, + + /* '[', '\', ']', '^', '_', '`', 'a', 'b', */ + -1, -1, -1, -1, -1, -1, 26, 27, + + /* 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', */ + 28, 29, 30, 31, 32, 33, 34, 35, + + /* 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', */ + 36, 37, 38, 39, 40, 41, 42, 43, + + /* 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', */ + 44, 45, 46, 47, 48, 49, 50, 51, +}; + +int base64url_encode(const unsigned char *in, unsigned int inlen, char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int s = i % 3; /* from 6/gcd(6, 8) */ + + switch (s) { + case 0: + out[j++] = base64en[(in[i] >> 2) & 0x3F]; + continue; + case 1: + out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)]; + continue; + case 2: + out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)]; + out[j++] = base64en[in[i] & 0x3F]; + } + } + + /* move back */ + i -= 1; + + /* check the last and add padding */ + + if ((i % 3) == 0) { + out[j++] = base64en[(in[i] & 0x3) << 4]; + //out[j++] = BASE64_PAD; + //out[j++] = BASE64_PAD; + } else if ((i % 3) == 1) { + out[j++] = base64en[(in[i] & 0xF) << 2]; + //out[j++] = BASE64_PAD; + } + + out[j++] = 0; + + return BASE64_OK; +} + +int base64url_decode(const char *in, unsigned int inlen, unsigned char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int c; + int s = i % 4; /* from 8/gcd(6, 8) */ + + if (in[i] == '=') + return BASE64_OK; + + if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || + (c = base64de[in[i] - BASE64DE_FIRST]) == -1) + return BASE64_INVALID; + + switch (s) { + case 0: + out[j] = ((unsigned int)c << 2) & 0xFF; + continue; + case 1: + out[j++] += ((unsigned int)c >> 4) & 0x3; + + /* if not last char with padding */ + if (i < (inlen - 3) || in[inlen - 2] != '=') + out[j] = ((unsigned int)c & 0xF) << 4; + continue; + case 2: + out[j++] += ((unsigned int)c >> 2) & 0xF; + + /* if not last char with padding */ + if (i < (inlen - 2) || in[inlen - 1] != '=') + out[j] = ((unsigned int)c & 0x3) << 6; + continue; + case 3: + out[j++] += (unsigned char)c; + } + } + + return BASE64_OK; +} diff --git a/cloud/GCP/JWT/base64url.h b/cloud/GCP/JWT/base64url.h new file mode 100644 index 00000000..7a8c80ca --- /dev/null +++ b/cloud/GCP/JWT/base64url.h @@ -0,0 +1,15 @@ +// https://raw.githubusercontent.com/zhicheng/base64/master/base64.h +#ifndef __BASE64URL_H__ +#define __BASE64URL_H__ + +enum {BASE64_OK = 0, BASE64_INVALID}; + +#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) +#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) + +int base64url_encode(const unsigned char *in, unsigned int inlen, char *out); + +int base64url_decode(const char *in, unsigned int inlen, unsigned char *out); + + +#endif /* __BASE64URL_H__ */ diff --git a/cloud/GCP/JWT/main.cpp b/cloud/GCP/JWT/main.cpp new file mode 100644 index 00000000..fcf8ec8b --- /dev/null +++ b/cloud/GCP/JWT/main.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "passwords.h" +#include "base64url.h" + +// This is an "xxd" file of the PEM of the private key. +#include "device1_private_pem.h" + + +extern "C" { + void app_main(); +} + +/** + * Return a string representation of an mbedtls error code + */ +static char* mbedtlsError(int errnum) { + static char buffer[200]; + mbedtls_strerror(errnum, buffer, sizeof(buffer)); + return buffer; +} // mbedtlsError + + +/** + * Create a JWT token for GCP. + * For full details, perform a Google search on JWT. However, in summary, we build two strings. One that represents the + * header and one that represents the payload. Both are JSON and are as described in the GCP and JWT documentation. Next + * we base64url encode both strings. Note that is distinct from normal/simple base64 encoding. Once we have a string for + * the base64url encoding of both header and payload, we concatenate both strings together separated by a ".". This resulting + * string is then signed using RSASSA which basically produces an SHA256 message digest that is then signed. The resulting + * binary is then itself converted into base64url and concatenated with the previously built base64url combined header and + * payload and that is our resulting JWT token. + * @param projectId The GCP project. + * @param privateKey The PEM or DER of the private key. + * @param privateKeySize The size in bytes of the private key. + * @returns A JWT token for transmission to GCP. + */ +char* createGCPJWT(const char* projectId, uint8_t* privateKey, size_t privateKeySize) { + char base64Header[100]; + const char header[] = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"; + base64url_encode( + (unsigned char *)header, // Data to encode. + strlen(header), // Length of data to encode. + base64Header); // Base64 encoded data. + + time_t now; + time(&now); + uint32_t iat = now; // Set the time now. + uint32_t exp = iat + 60*60; // Set the expiry time. + + char payload[100]; + sprintf(payload, "{\"iat\":%d,\"exp\":%d,\"aud\":\"%s\"}", iat, exp, projectId); + + char base64Payload[100]; + base64url_encode( + (unsigned char *)payload, // Data to encode. + strlen(payload), // Length of data to encode. + base64Payload); // Base64 encoded data. + + uint8_t headerAndPayload[800]; + sprintf((char*)headerAndPayload, "%s.%s", base64Header, base64Payload); + + // At this point we have created the header and payload parts, converted both to base64 and concatenated them + // together as a single string. Now we need to sign them using RSASSA + + mbedtls_pk_context pk_context; + mbedtls_pk_init(&pk_context); + int rc = mbedtls_pk_parse_key(&pk_context, privateKey, privateKeySize, NULL, 0); + if (rc != 0) { + printf("Failed to mbedtls_pk_parse_key: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc)); + return nullptr; + } + + uint8_t oBuf[5000]; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + + const char* pers="MyEntropy"; + + mbedtls_ctr_drbg_seed( + &ctr_drbg, + mbedtls_entropy_func, + &entropy, + (const unsigned char*)pers, + strlen(pers)); + + + uint8_t digest[32]; + rc = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), headerAndPayload, strlen((char*)headerAndPayload), digest); + if (rc != 0) { + printf("Failed to mbedtls_md: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc)); + return nullptr; + } + + size_t retSize; + rc = mbedtls_pk_sign(&pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), oBuf, &retSize, mbedtls_ctr_drbg_random, &ctr_drbg); + if (rc != 0) { + printf("Failed to mbedtls_pk_sign: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc)); + return nullptr; + } + + + char base64Signature[600]; + base64url_encode((unsigned char *)oBuf, retSize, base64Signature); + + char* retData = (char*)malloc(strlen((char*)headerAndPayload) + 1 + strlen((char*)base64Signature) + 1); + + sprintf(retData, "%s.%s", headerAndPayload, base64Signature); + + mbedtls_pk_free(&pk_context); + return retData; +} + +void run(void *) { + printf("Task starting!\n"); + const char* projectId = "test-214415"; + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "time-a-g.nist.gov"); + sntp_init(); + // https://www.epochconverter.com/ + time_t now = 0; + time(&now); + while(now < 5000) { + vTaskDelay(1000 * portTICK_RATE_MS); + time(&now); + } + + char* jwt = createGCPJWT(projectId, device1_private_pem, device1_private_pem_len); + if (jwt != nullptr) { + printf("JWT: %s\n", jwt); + free(jwt); + } + vTaskDelete(nullptr); +} + +esp_err_t event_handler(void *ctx, system_event_t *event) +{ + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { + printf("Our IP address is " IPSTR "\n", + IP2STR(&event->event_info.got_ip.ip_info.ip)); + printf("We have now connected to a station and can do things...\n"); + xTaskCreate(run, "run", 16000, nullptr, 0, nullptr); + + } + + if (event->event_id == SYSTEM_EVENT_STA_START) { + ESP_ERROR_CHECK(esp_wifi_connect()); + } + return ESP_OK; +} + +void app_main(void) +{ + printf("Starting\n"); + nvs_flash_init(); + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + wifi_config_t sta_config; + memset(&sta_config, 0, sizeof(sta_config)); + strcpy((char*)sta_config.sta.ssid, SSID); + strcpy((char*)sta_config.sta.password, SSID_PASSWORD); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + From 1d976d21e95eacb8773ebc45c6c0b3f7f8897c77 Mon Sep 17 00:00:00 2001 From: menesesleonardo <33615982+menesesleonardo@users.noreply.github.com> Date: Tue, 28 Aug 2018 23:55:46 -0500 Subject: [PATCH 241/310] Create pcf8523 it is very similar to ds1307 and people could use it. --- hardware/rtc/pcf8523.c | 200 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 hardware/rtc/pcf8523.c diff --git a/hardware/rtc/pcf8523.c b/hardware/rtc/pcf8523.c new file mode 100644 index 00000000..2d1e310e --- /dev/null +++ b/hardware/rtc/pcf8523.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "errorhandle_func.h" + +#include "sdkconfig.h" + +#define SDA_PIN 23 +#define SCL_PIN 22 +#define RTC_ADDRESS 0x68 // most I2C rtcs have their address on 0x68. any doubt check with i2c scanner snippet + +static char tag[] = "RTC"; + +static uint8_t intToBCD(uint8_t num) { + return ((num / 10) << 4) | (num%10); +} + +static uint8_t bcdToInt(uint8_t bcd) { + // 0x10 + return ((bcd >> 4) * 10) + (bcd & 0x0f);; +} + + +void initI2C() { + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = SDA_PIN; + conf.scl_io_num = SCL_PIN; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = 100000; + ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); + ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); +} + +/* + * PCF8523 slightly changed its 7 bytes encoded in BCD: + * 03h - Seconds - 00-59 + * 04h - Minutes - 00-59 + * 05h - Hours - 00-23 + * 06h - monthday - 01-31 + * 07h - weekday - 00-06 + * 08h - Month - 01-12 + * 09h - Year - 00-99 + * + */ +time_t rtc_readValue() { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_WRITE, true /* expect ack */)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, 0x03, 1)); // start address + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_READ, true /* expect ack */)); + uint8_t data[7]; + ESP_ERROR_CHECK(i2c_master_read(cmd, data, 7, false)); + ESP_ERROR_CHECK(i2c_master_stop(cmd)); + COMMANDCHECKOKERR(i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000/portTICK_PERIOD_MS),"RTC COMMAND"); + i2c_cmd_link_delete(cmd); + + int i; + for (i=0; i<7; i++) { + ESP_LOGD(tag, "%d: 0x%.2x", i, data[i]); + } + + struct tm tm; + tm.tm_sec = bcdToInt(data[0]); + tm.tm_min = bcdToInt(data[1]); + tm.tm_hour = bcdToInt(data[2]); + tm.tm_mday = bcdToInt(data[3]); + tm.tm_mon = bcdToInt(data[5]) - 1; // 0-11 - Note: The month on the PCF8523 is 1-12. + tm.tm_year = bcdToInt(data[6]) + 100; // Years since 1900 + time_t readTime = mktime(&tm); + return readTime; +} + +void rtc_writeValue(time_t newTime) { + ESP_LOGD(tag, ">> writeValue: %ld", newTime); + struct tm tm; + gmtime_r(&newTime, &tm); + char buf[30]; + ESP_LOGD(tag, " - %s", asctime_r(&tm, buf)); + + esp_err_t errRc; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, 0x03, 1)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_sec), 1)); // seconds + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_min), 1 )); // minutes + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_hour), 1 )); // hours + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_mday), 1)); // date of month + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_wday+1), 1 )); // week day + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_mon+1), 1)); // month + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_year-100), 1)); // year + ESP_ERROR_CHECK(i2c_master_stop(cmd)); + errRc = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000/portTICK_PERIOD_MS); + if (errRc != 0) { + ESP_LOGE(tag, "i2c_master_cmd_begin: %d", errRc); + } + i2c_cmd_link_delete(cmd); +} + + +/* + implement in your time function + @Kolban created a nice one, here is my contribution. + + esp_err_t sntp_update(){ + + static const char *tag = "TIME_SETUP"; + esp_err_t ret; + char buffer[20]; + EventBits_t bitreturn; + TickType_t waittime = 10000/portTICK_PERIOD_MS; + + + bitreturn = xEventGroupWaitBits(event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, waittime); + + if((bitreturn & BIT0) != 0){ + + printf("going online\n"); + + } + else printf("going offline\n"); + + + // initialize the SNTP service + sntp_setoperatingmode(SNTP_OPMODE_POLL); + // to create SNTP server variable make a #define statement i.e. "pool.ntp.org" + sntp_setservername(0, CONFIG_SNTP_SERVER); + sntp_init(); + + initI2C(); + + time_t t; + struct tm timertc; + t = rtc_readValue(); + localtime_r(&t, &timertc); + + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timertc); + ESP_LOGI(tag,"Current time in rtc (GTM) is: %s\n\n", buffer); + + // wait for the service to set the time + time_t now; + struct tm timeinfo; + time(&now); + localtime_r(&now, &timeinfo); + int counter = 0; + + // try for a minute to get time from network + while((timeinfo.tm_year < (2018 - 1900)) && (counter < 12)) + { + + ESP_LOGW(tag,"Time outdated, waiting...\n"); + vTaskDelay(5000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + counter ++; + } + + if((timeinfo.tm_year < (2018 - 1900)) && (counter == 12)){ + ESP_LOGE(tag, "TIMEOUT"); + + + // stick to rtc time + now = rtc_readValue(); + + } + else{ + // update rtc time + rtc_writeValue(now); + } + + + // to create timezone variable make a #define statement i.e. "COT+5" + setenv("TZ",CONFIG_TIMEZONE_TZ, 1); + tzset(); + + // print the actual time in location + localtime_r(&now, &timeinfo); + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timeinfo); + ESP_LOGI(tag,"Current time in your Location: %s\n\n", buffer); + + // check time set in rtc in case it was updated from network + t = rtc_readValue(); + localtime_r(&t, &timertc); + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timertc); + ESP_LOGI(tag,"Current time in rtc (GTM) is: %s\n\n", buffer); + + ret = ESP_OK; + + return ret; +} + + */ From f8d3438005b0db8c1db6b7d092569902b92ae8d9 Mon Sep 17 00:00:00 2001 From: SolipsistD Date: Mon, 3 Sep 2018 13:16:02 +0100 Subject: [PATCH 242/310] Fix params passed to i2c.init() to include DEVICE_ADDRESS in the first position. Otherwise SDA becomes SCL_PIN and SCL becomes Default. --- cpp_utils/tests/task_i2c_scanner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp_utils/tests/task_i2c_scanner.cpp b/cpp_utils/tests/task_i2c_scanner.cpp index b1bde2e0..061c3b08 100644 --- a/cpp_utils/tests/task_i2c_scanner.cpp +++ b/cpp_utils/tests/task_i2c_scanner.cpp @@ -6,6 +6,7 @@ #include "sdkconfig.h" +#define DEVICE_ADDRESS 0 #define SDA_PIN 25 #define SCL_PIN 26 @@ -14,7 +15,7 @@ class I2CScanner: public Task { void run(void *data) override { I2C i2c; - i2c.init((gpio_num_t)SDA_PIN, (gpio_num_t)SCL_PIN); + i2c.init((uint8_t)DEVICE_ADDRESS, (gpio_num_t)SDA_PIN, (gpio_num_t)SCL_PIN); i2c.scan(); } // End run }; From b0503a6489da57bb40557b86c282d9ab4b225238 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 5 Sep 2018 14:54:42 +0200 Subject: [PATCH 243/310] add multiple servers support --- cpp_utils/BLEClient.cpp | 8 +- cpp_utils/BLEClient.h | 2 +- cpp_utils/BLEDevice.cpp | 1106 +++++++++--------- cpp_utils/BLEDevice.h | 5 +- cpp_utils/BLERemoteDescriptor.cpp | 370 +++--- cpp_utils/BLEScan.cpp | 584 +++++----- cpp_utils/BLEScan.h | 158 +-- cpp_utils/BLESecurity.h | 142 +-- cpp_utils/BLEService.cpp | 870 +++++++------- cpp_utils/BLEService.h | 208 ++-- cpp_utils/BLEServiceMap.cpp | 264 ++--- cpp_utils/WiFi.cpp | 1808 ++++++++++++++--------------- cpp_utils/WiFi.h | 316 ++--- 13 files changed, 2928 insertions(+), 2913 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 141cf0f5..14198d18 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -21,6 +21,7 @@ #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif +#include "BLEDevice.h" /* * Design @@ -44,9 +45,9 @@ */ static const char* LOG_TAG = "BLEClient"; -BLEClient::BLEClient() { +BLEClient::BLEClient(uint16_t connID) { m_pClientCallbacks = nullptr; - m_conn_id = 0; + m_conn_id = connID; m_gattc_if = 0; m_haveServices = false; m_isConnected = false; // Initially, we are flagged as not connected. @@ -96,7 +97,7 @@ bool BLEClient::connect(BLEAddress address) { clearServices(); // Delete any services that may exist. - esp_err_t errRc = ::esp_ble_gattc_app_register(0); + esp_err_t errRc = ::esp_ble_gattc_app_register(m_conn_id); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; @@ -138,6 +139,7 @@ void BLEClient::disconnect() { } esp_ble_gattc_app_unregister(getGattcIf()); m_peerAddress = BLEAddress("00:00:00:00:00:00"); + BLEDevice::removeClient(m_conn_id); ESP_LOGD(LOG_TAG, "<< disconnect()"); } // disconnect diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index a60ed102..f010b05f 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -28,7 +28,7 @@ class BLEClientCallbacks; */ class BLEClient { public: - BLEClient(); + BLEClient(uint16_t connID); ~BLEClient(); bool connect(BLEAddress address); // Connect to the remote BLE Server diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index a7db454b..369b1285 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -1,548 +1,558 @@ -/* - * BLE.cpp - * - * Created on: Mar 16, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include -#include -#include -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 ESP-IDF -#include // ESP32 ESP-IDF -#include // Part of C++ Standard library -#include // Part of C++ Standard library -#include // Part of C++ Standard library - -#include "BLEDevice.h" -#include "BLEClient.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#include "esp32-hal-bt.h" -#endif - -static const char* LOG_TAG = "BLEDevice"; - -/** - * Singletons for the BLEDevice. - */ -BLEServer* BLEDevice::m_pServer = nullptr; -BLEScan* BLEDevice::m_pScan = nullptr; -BLEClient* BLEDevice::m_pClient = nullptr; -bool initialized = false; // Have we been initialized? -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; -BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; -uint16_t BLEDevice::m_localMTU = 23; - -/** - * @brief Create a new instance of a client. - * @return A new instance of the client. - */ -/* STATIC */ BLEClient* BLEDevice::createClient() { - ESP_LOGD(LOG_TAG, ">> createClient"); -#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig - ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); - abort(); -#endif // CONFIG_GATTC_ENABLE - m_pClient = new BLEClient(); - ESP_LOGD(LOG_TAG, "<< createClient"); - return m_pClient; -} // createClient - - -/** - * @brief Create a new instance of a server. - * @return A new instance of the server. - */ -/* STATIC */ BLEServer* BLEDevice::createServer() { - ESP_LOGD(LOG_TAG, ">> createServer"); -#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig - ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); - abort(); -#endif // CONFIG_GATTS_ENABLE - m_pServer = new BLEServer(); - m_pServer->createApp(0); - ESP_LOGD(LOG_TAG, "<< createServer"); - return m_pServer; -} // createServer - - -/** - * @brief Handle GATT server events. - * - * @param [in] event The event that has been newly received. - * @param [in] gatts_if The connection to the GATT interface. - * @param [in] param Parameters for the event. - */ -/* STATIC */ void BLEDevice::gattServerEventHandler( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param -) { - ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", - gatts_if, - BLEUtils::gattServerEventTypeToString(event).c_str()); - - BLEUtils::dumpGattServerEvent(event, gatts_if, param); - - switch(event) { - case ESP_GATTS_CONNECT_EVT: { - BLEDevice::m_localMTU = 23; -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - } // ESP_GATTS_CONNECT_EVT - - case ESP_GATTS_MTU_EVT: { - BLEDevice::m_localMTU = param->mtu.mtu; - ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); - break; - } - default: { - break; - } - } // switch - - - if (BLEDevice::m_pServer != nullptr) { - BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); - } -} // gattServerEventHandler - - -/** - * @brief Handle GATT client events. - * - * Handler for the GATT client events. - * - * @param [in] event - * @param [in] gattc_if - * @param [in] param - */ -/* STATIC */ void BLEDevice::gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* param) { - - ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", - gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); - BLEUtils::dumpGattClientEvent(event, gattc_if, param); - - switch(event) { - case ESP_GATTC_CONNECT_EVT: { - if(BLEDevice::getMTU() != 23){ - esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - } // ESP_GATTC_CONNECT_EVT - - default: { - break; - } - } // switch - - - // If we have a client registered, call it. - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); - } - -} // gattClientEventHandler - - -/** - * @brief Handle GAP events. - */ -/* STATIC */ void BLEDevice::gapEventHandler( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param) { - - BLEUtils::dumpGapEvent(event, param); - - switch(event) { - - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); - break; - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); - break; - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); - break; - case ESP_GAP_BLE_NC_REQ_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); - // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - /* - * TODO should we add white/black list comparison? - */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); - } - else{ - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - /* - * - */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - ///show the passkey number to the user to input it in the peer deivce. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); - BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key type info share with peer device to the user. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - default: { - break; - } - } // switch - - if (BLEDevice::m_pServer != nullptr) { - BLEDevice::m_pServer->handleGAPEvent(event, param); - } - - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->handleGAPEvent(event, param); - } - - if (BLEDevice::m_pScan != nullptr) { - BLEDevice::getScan()->handleGAPEvent(event, param); - } - - /* - * Security events: - */ - - -} // gapEventHandler - - -/** - * @brief Get the BLE device address. - * @return The BLE device address. - */ -/* STATIC*/ BLEAddress BLEDevice::getAddress() { - const uint8_t* bdAddr = esp_bt_dev_get_address(); - esp_bd_addr_t addr; - memcpy(addr, bdAddr, sizeof(addr)); - return BLEAddress(addr); -} // getAddress - - -/** - * @brief Retrieve the Scan object that we use for scanning. - * @return The scanning object reference. This is a singleton object. The caller should not - * try and release/delete it. - */ -/* STATIC */ BLEScan* BLEDevice::getScan() { - //ESP_LOGD(LOG_TAG, ">> getScan"); - if (m_pScan == nullptr) { - m_pScan = new BLEScan(); - //ESP_LOGD(LOG_TAG, " - creating a new scan object"); - } - //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); - return m_pScan; -} // getScan - - -/** - * @brief Get the value of a characteristic of a service on a remote device. - * @param [in] bdAddress - * @param [in] serviceUUID - * @param [in] characteristicUUID - */ -/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { - ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); - pClient->connect(bdAddress); - std::string ret = pClient->getValue(serviceUUID, characteristicUUID); - pClient->disconnect(); - ESP_LOGD(LOG_TAG, "<< getValue"); - return ret; -} // getValue - - -/** - * @brief Initialize the %BLE environment. - * @param deviceName The device name of the device. - */ -/* STATIC */ void BLEDevice::init(std::string deviceName) { - if(!initialized){ - initialized = true; // Set the initialization flag to ensure we are only initialized once. - - esp_err_t errRc = ESP_OK; -#ifdef ARDUINO_ARCH_ESP32 - if (!btStart()) { - errRc = ESP_FAIL; - return; - } -#else - errRc = ::nvs_flash_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - errRc = esp_bt_controller_init(&bt_cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - -#ifndef CLASSIC_BT_ENABLED - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue - errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#else - errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif -#endif - - esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); - if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ - errRc = esp_bluedroid_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ - errRc = esp_bluedroid_enable(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - -#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig - errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif // CONFIG_GATTC_ENABLE - -#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig - errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif // CONFIG_GATTS_ENABLE - - errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; - -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; - errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; -#endif // CONFIG_BLE_SMP_ENABLE - } - vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. -} // init - - -/** - * @brief Set the transmission power. - * The power level can be one of: - * * ESP_PWR_LVL_N14 - * * ESP_PWR_LVL_N11 - * * ESP_PWR_LVL_N8 - * * ESP_PWR_LVL_N5 - * * ESP_PWR_LVL_N2 - * * ESP_PWR_LVL_P1 - * * ESP_PWR_LVL_P4 - * * ESP_PWR_LVL_P7 - * @param [in] powerLevel. - */ -/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { - ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); - esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - }; - ESP_LOGD(LOG_TAG, "<< setPower"); -} // setPower - - -/** - * @brief Set the value of a characteristic of a service on a remote device. - * @param [in] bdAddress - * @param [in] serviceUUID - * @param [in] characteristicUUID - */ -/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { - ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); - pClient->connect(bdAddress); - pClient->setValue(serviceUUID, characteristicUUID, value); - pClient->disconnect(); -} // setValue - - -/** - * @brief Return a string representation of the nature of this device. - * @return A string representation of the nature of this device. - */ -/* STATIC */ std::string BLEDevice::toString() { - std::ostringstream oss; - oss << "BD Address: " << getAddress().toString(); - return oss.str(); -} // toString - - -/** - * @brief Add an entry to the BLE white list. - * @param [in] address The address to add to the white list. - */ -void BLEDevice::whiteListAdd(BLEAddress address) { - ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); - esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - ESP_LOGD(LOG_TAG, "<< whiteListAdd"); -} // whiteListAdd - - -/** - * @brief Remove an entry from the BLE white list. - * @param [in] address The address to remove from the white list. - */ -void BLEDevice::whiteListRemove(BLEAddress address) { - ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); - esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - ESP_LOGD(LOG_TAG, "<< whiteListRemove"); -} // whiteListRemove - -/* - * @brief Set encryption level that will be negotiated with peer device durng connection - * @param [in] level Requested encryption level - */ -void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { - BLEDevice::m_securityLevel = level; -} - -/* - * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events - * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback - */ -void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { - BLEDevice::m_securityCallbacks = callbacks; -} - -/* - * @brief Setup local mtu that will be used to negotiate mtu during request from client peer - * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 - */ -esp_err_t BLEDevice::setMTU(uint16_t mtu) { - ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); - esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); - if(err == ESP_OK){ - m_localMTU = mtu; - } else { - ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); - } - ESP_LOGD(LOG_TAG, "<< setLocalMTU"); - return err; -} - -/* - * @brief Get local MTU value set during mtu request or default value - */ -uint16_t BLEDevice::getMTU() { - return m_localMTU; -} - -bool BLEDevice::getInitialized() { - return initialized; -} -#endif // CONFIG_BT_ENABLED +/* + * BLE.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 ESP-IDF +#include // ESP32 ESP-IDF +#include // Part of C++ Standard library +#include // Part of C++ Standard library +#include // Part of C++ Standard library + +#include "BLEDevice.h" +#include "BLEClient.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#include "esp32-hal-bt.h" +#endif + +static const char* LOG_TAG = "BLEDevice"; + +/** + * Singletons for the BLEDevice. + */ +BLEServer* BLEDevice::m_pServer = nullptr; +BLEScan* BLEDevice::m_pScan = nullptr; +BLEClient* BLEDevice::m_pClient = nullptr; +bool initialized = false; // Have we been initialized? +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; +BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; +uint16_t BLEDevice::m_localMTU = 23; + +/** + * @brief Create a new instance of a client. + * @return A new instance of the client. + */ +/* STATIC */ BLEClient* BLEDevice::createClient(uint16_t connID) { + ESP_LOGD(LOG_TAG, ">> createClient"); +#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTC_ENABLE + m_pClient = new BLEClient(connID); + addClient(connID, m_pClient); + ESP_LOGD(LOG_TAG, "<< createClient"); + return m_pClient; +} // createClient + + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +/* STATIC */ BLEServer* BLEDevice::createServer() { + ESP_LOGD(LOG_TAG, ">> createServer"); +#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE + m_pServer = new BLEServer(); + m_pServer->createApp(0); + ESP_LOGD(LOG_TAG, "<< createServer"); + return m_pServer; +} // createServer + + +/** + * @brief Handle GATT server events. + * + * @param [in] event The event that has been newly received. + * @param [in] gatts_if The connection to the GATT interface. + * @param [in] param Parameters for the event. + */ +/* STATIC */ void BLEDevice::gattServerEventHandler( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param +) { + ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", + gatts_if, + BLEUtils::gattServerEventTypeToString(event).c_str()); + + BLEUtils::dumpGattServerEvent(event, gatts_if, param); + + switch(event) { + case ESP_GATTS_CONNECT_EVT: { + BLEDevice::m_localMTU = 23; +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTS_CONNECT_EVT + + case ESP_GATTS_MTU_EVT: { + BLEDevice::m_localMTU = param->mtu.mtu; + ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); + break; + } + default: { + break; + } + } // switch + + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); + } +} // gattServerEventHandler + + +/** + * @brief Handle GATT client events. + * + * Handler for the GATT client events. + * + * @param [in] event + * @param [in] gattc_if + * @param [in] param + */ +/* STATIC */ void BLEDevice::gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + BLEUtils::dumpGattClientEvent(event, gattc_if, param); + + switch(event) { + case ESP_GATTC_CONNECT_EVT: { + if(BLEDevice::getMTU() != 23){ + esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTC_CONNECT_EVT + + default: { + break; + } + } // switch + + + // If we have a client registered, call it. + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); + } + +} // gattClientEventHandler + + +/** + * @brief Handle GAP events. + */ +/* STATIC */ void BLEDevice::gapEventHandler( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t *param) { + + BLEUtils::dumpGapEvent(event, param); + + switch(event) { + + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); + } + else{ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer deivce. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + default: { + break; + } + } // switch + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGAPEvent(event, param); + } + + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->handleGAPEvent(event, param); + } + + if (BLEDevice::m_pScan != nullptr) { + BLEDevice::getScan()->handleGAPEvent(event, param); + } + + /* + * Security events: + */ + + +} // gapEventHandler + + +/** + * @brief Get the BLE device address. + * @return The BLE device address. + */ +/* STATIC*/ BLEAddress BLEDevice::getAddress() { + const uint8_t* bdAddr = esp_bt_dev_get_address(); + esp_bd_addr_t addr; + memcpy(addr, bdAddr, sizeof(addr)); + return BLEAddress(addr); +} // getAddress + + +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. + */ +/* STATIC */ BLEScan* BLEDevice::getScan() { + //ESP_LOGD(LOG_TAG, ">> getScan"); + if (m_pScan == nullptr) { + m_pScan = new BLEScan(); + //ESP_LOGD(LOG_TAG, " - creating a new scan object"); + } + //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); + return m_pScan; +} // getScan + + +/** + * @brief Get the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + std::string ret = pClient->getValue(serviceUUID, characteristicUUID); + pClient->disconnect(); + ESP_LOGD(LOG_TAG, "<< getValue"); + return ret; +} // getValue + + +/** + * @brief Initialize the %BLE environment. + * @param deviceName The device name of the device. + */ +/* STATIC */ void BLEDevice::init(std::string deviceName) { + if(!initialized){ + initialized = true; // Set the initialization flag to ensure we are only initialized once. + + esp_err_t errRc = ESP_OK; +#ifdef ARDUINO_ARCH_ESP32 + if (!btStart()) { + errRc = ESP_FAIL; + return; + } +#else + errRc = ::nvs_flash_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + errRc = esp_bt_controller_init(&bt_cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifndef CLASSIC_BT_ENABLED + // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#else + errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif +#endif + + esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); + if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ + errRc = esp_bluedroid_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ + errRc = esp_bluedroid_enable(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig + errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTC_ENABLE + +#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig + errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTS_ENABLE + + errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; + +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; + errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; +#endif // CONFIG_BLE_SMP_ENABLE + } + vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. +} // init + + +/** + * @brief Set the transmission power. + * The power level can be one of: + * * ESP_PWR_LVL_N14 + * * ESP_PWR_LVL_N11 + * * ESP_PWR_LVL_N8 + * * ESP_PWR_LVL_N5 + * * ESP_PWR_LVL_N2 + * * ESP_PWR_LVL_P1 + * * ESP_PWR_LVL_P4 + * * ESP_PWR_LVL_P7 + * @param [in] powerLevel. + */ +/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { + ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); + esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + }; + ESP_LOGD(LOG_TAG, "<< setPower"); +} // setPower + + +/** + * @brief Set the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + pClient->setValue(serviceUUID, characteristicUUID, value); + pClient->disconnect(); +} // setValue + + +/** + * @brief Return a string representation of the nature of this device. + * @return A string representation of the nature of this device. + */ +/* STATIC */ std::string BLEDevice::toString() { + std::ostringstream oss; + oss << "BD Address: " << getAddress().toString(); + return oss.str(); +} // toString + + +/** + * @brief Add an entry to the BLE white list. + * @param [in] address The address to add to the white list. + */ +void BLEDevice::whiteListAdd(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListAdd"); +} // whiteListAdd + + +/** + * @brief Remove an entry from the BLE white list. + * @param [in] address The address to remove from the white list. + */ +void BLEDevice::whiteListRemove(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListRemove"); +} // whiteListRemove + +/* + * @brief Set encryption level that will be negotiated with peer device durng connection + * @param [in] level Requested encryption level + */ +void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { + BLEDevice::m_securityLevel = level; +} + +/* + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback + */ +void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + BLEDevice::m_securityCallbacks = callbacks; +} + +/* + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer + * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 + */ +esp_err_t BLEDevice::setMTU(uint16_t mtu) { + ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); + if(err == ESP_OK){ + m_localMTU = mtu; + } else { + ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); + } + ESP_LOGD(LOG_TAG, "<< setLocalMTU"); + return err; +} + +/* + * @brief Get local MTU value set during mtu request or default value + */ +uint16_t BLEDevice::getMTU() { + return m_localMTU; +} + +bool BLEDevice::getInitialized() { + return initialized; +} + +void BLEDevice::addClient(uint16_t connID, BLEClient* client) { + // m_clientList.insert(std::pair(connID, client)); +} + +void BLEDevice::removeClient(uint16_t connID) { + m_clientList.erase(connID); +} + +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 7a1b833d..e4f4ff5a 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -27,7 +27,7 @@ class BLEDevice { public: - static BLEClient* createClient(); // Create a new BLE client. + static BLEClient* createClient(uint16_t connID = 0); // Create a new BLE client. static BLEServer* createServer(); // Cretae a new BLE server. static BLEAddress getAddress(); // Retrieve our own local BD address. static BLEScan* getScan(); // Get the scan object @@ -43,6 +43,8 @@ class BLEDevice { static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); static bool getInitialized(); // Returns the state of the device, is it initialized or not? + static void addClient(uint16_t connID, BLEClient* client); + static void removeClient(uint16_t connID); private: static BLEServer *m_pServer; @@ -51,6 +53,7 @@ class BLEDevice { static esp_ble_sec_act_t m_securityLevel; static BLESecurityCallbacks* m_securityCallbacks; static uint16_t m_localMTU; + static std::map m_clientList; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 2cdc7db1..36492360 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -1,185 +1,185 @@ -/* - * BLERemoteDescriptor.cpp - * - * Created on: Jul 8, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include "BLERemoteDescriptor.h" -#include "GeneralUtils.h" -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -static const char* LOG_TAG = "BLERemoteDescriptor"; - - -BLERemoteDescriptor::BLERemoteDescriptor( - uint16_t handle, - BLEUUID uuid, - BLERemoteCharacteristic* pRemoteCharacteristic) { - - m_handle = handle; - m_uuid = uuid; - m_pRemoteCharacteristic = pRemoteCharacteristic; -} - - -/** - * @brief Retrieve the handle associated with this remote descriptor. - * @return The handle associated with this remote descriptor. - */ -uint16_t BLERemoteDescriptor::getHandle() { - return m_handle; -} // getHandle - - -/** - * @brief Get the characteristic that owns this descriptor. - * @return The characteristic that owns this descriptor. - */ -BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { - return m_pRemoteCharacteristic; -} // getRemoteCharacteristic - - -/** - * @brief Retrieve the UUID associated this remote descriptor. - * @return The UUID associated this remote descriptor. - */ -BLEUUID BLERemoteDescriptor::getUUID() { - return m_uuid; -} // getUUID - - -std::string BLERemoteDescriptor::readValue(void) { - ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); - - // Check to see that we are connected. - if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { - ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); - } - - m_semaphoreReadDescrEvt.take("readValue"); - - // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. - esp_err_t errRc = ::esp_ble_gattc_read_char_descr( - m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), - m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server - getHandle(), // The handle of this characteristic - ESP_GATT_AUTH_REQ_NONE); // Security - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return ""; - } - - // Block waiting for the event that indicates that the read has completed. When it has, the std::string found - // in m_value will contain our data. - m_semaphoreReadDescrEvt.wait("readValue"); - - ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); - return m_value; -} // readValue - - -uint8_t BLERemoteDescriptor::readUInt8(void) { - std::string value = readValue(); - if (value.length() >= 1) { - return (uint8_t)value[0]; - } - return 0; -} // readUInt8 - - -uint16_t BLERemoteDescriptor::readUInt16(void) { - std::string value = readValue(); - if (value.length() >= 2) { - return *(uint16_t*)(value.data()); - } - return 0; -} // readUInt16 - - -uint32_t BLERemoteDescriptor::readUInt32(void) { - std::string value = readValue(); - if (value.length() >= 4) { - return *(uint32_t*)(value.data()); - } - return 0; -} // readUInt32 - - -/** - * @brief Return a string representation of this BLE Remote Descriptor. - * @retun A string representation of this BLE Remote Descriptor. - */ -std::string BLERemoteDescriptor::toString(void) { - std::stringstream ss; - ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); - return ss.str(); -} // toString - - -/** - * @brief Write data to the BLE Remote Descriptor. - * @param [in] data The data to send to the remote descriptor. - * @param [in] length The length of the data to send. - * @param [in] response True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - uint8_t* data, - size_t length, - bool response) { - ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); - // Check to see that we are connected. - if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { - ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); - } - - esp_err_t errRc = ::esp_ble_gattc_write_char_descr( - m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), - m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), - getHandle(), - length, // Data length - data, // Data - response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE - ); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); - } - ESP_LOGD(LOG_TAG, "<< writeValue"); -} // writeValue - - -/** - * @brief Write data represented as a string to the BLE Remote Descriptor. - * @param [in] newValue The data to send to the remote descriptor. - * @param [in] response True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - std::string newValue, - bool response) { - writeValue(newValue.data(), newValue.length()); -} // writeValue - - -/** - * @brief Write a byte value to the Descriptor. - * @param [in] The single byte to write. - * @param [in] True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - uint8_t newValue, - bool response) { - writeValue(&newValue, 1, response); -} // writeValue - - -#endif /* CONFIG_BT_ENABLED */ +/* + * BLERemoteDescriptor.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLERemoteDescriptor.h" +#include "GeneralUtils.h" +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +static const char* LOG_TAG = "BLERemoteDescriptor"; + + +BLERemoteDescriptor::BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic) { + + m_handle = handle; + m_uuid = uuid; + m_pRemoteCharacteristic = pRemoteCharacteristic; +} + + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t BLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +BLEUUID BLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +std::string BLERemoteDescriptor::readValue(void) { + ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); + + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + m_semaphoreReadDescrEvt.take("readValue"); + + // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + esp_err_t errRc = ::esp_ble_gattc_read_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return ""; + } + + // Block waiting for the event that indicates that the read has completed. When it has, the std::string found + // in m_value will contain our data. + m_semaphoreReadDescrEvt.wait("readValue"); + + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return m_value; +} // readValue + + +uint8_t BLERemoteDescriptor::readUInt8(void) { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t)value[0]; + } + return 0; +} // readUInt8 + + +uint16_t BLERemoteDescriptor::readUInt16(void) { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*)(value.data()); + } + return 0; +} // readUInt16 + + +uint32_t BLERemoteDescriptor::readUInt32(void) { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*)(value.data()); + } + return 0; +} // readUInt32 + + +/** + * @brief Return a string representation of this BLE Remote Descriptor. + * @retun A string representation of this BLE Remote Descriptor. + */ +std::string BLERemoteDescriptor::toString(void) { + std::stringstream ss; + ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); + return ss.str(); +} // toString + + +/** + * @brief Write data to the BLE Remote Descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t* data, + size_t length, + bool response) { + ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + esp_err_t errRc = ::esp_ble_gattc_write_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), + getHandle(), + length, // Data length + data, // Data + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); + } + ESP_LOGD(LOG_TAG, "<< writeValue"); +} // writeValue + + +/** + * @brief Write data represented as a string to the BLE Remote Descriptor. + * @param [in] newValue The data to send to the remote descriptor. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + std::string newValue, + bool response) { + writeValue(newValue.data(), newValue.length()); +} // writeValue + + +/** + * @brief Write a byte value to the Descriptor. + * @param [in] The single byte to write. + * @param [in] True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t newValue, + bool response) { + writeValue(&newValue, 1, response); +} // writeValue + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 3046b7c8..87046670 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -1,292 +1,292 @@ -/* - * BLEScan.cpp - * - * Created on: Jul 1, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - - -#include -#include - -#include - -#include "BLEAdvertisedDevice.h" -#include "BLEScan.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -static const char* LOG_TAG = "BLEScan"; - - -/** - * Constructor - */ -BLEScan::BLEScan() { - m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. - m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; - m_pAdvertisedDeviceCallbacks = nullptr; - m_stopped = true; - m_wantDuplicates = false; - setInterval(100); - setWindow(100); -} // BLEScan - - -/** - * @brief Handle GAP events related to scans. - * @param [in] event The event type for this event. - * @param [in] param Parameter data for this event. - */ -void BLEScan::handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { - - switch(event) { - - // ESP_GAP_BLE_SCAN_RESULT_EVT - // --------------------------- - // scan_rst: - // esp_gap_search_evt_t search_evt - // esp_bd_addr_t bda - // esp_bt_dev_type_t dev_type - // esp_ble_addr_type_t ble_addr_type - // esp_ble_evt_type_t ble_evt_type - // int rssi - // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] - // int flag - // int num_resps - // uint8_t adv_data_len - // uint8_t scan_rsp_len - case ESP_GAP_BLE_SCAN_RESULT_EVT: { - - switch(param->scan_rst.search_evt) { - // - // ESP_GAP_SEARCH_INQ_CMPL_EVT - // - // Event that indicates that the duration allowed for the search has completed or that we have been - // asked to stop. - case ESP_GAP_SEARCH_INQ_CMPL_EVT: { - m_stopped = true; - if (m_scanCompleteCB != nullptr) { - m_scanCompleteCB(m_scanResults); - } - m_semaphoreScanEnd.give(); - break; - } // ESP_GAP_SEARCH_INQ_CMPL_EVT - - // - // ESP_GAP_SEARCH_INQ_RES_EVT - // - // Result that has arrived back from a Scan inquiry. - case ESP_GAP_SEARCH_INQ_RES_EVT: { - if (m_stopped) { // If we are not scanning, nothing to do with the extra results. - break; - } - -// Examine our list of previously scanned addresses and, if we found this one already, -// ignore it. - BLEAddress advertisedAddress(param->scan_rst.bda); - bool found = false; - - for (int i=0; iscan_rst.rssi); - advertisedDevice.setAdFlag(param->scan_rst.flag); - advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); - advertisedDevice.setScan(this); - - if (m_pAdvertisedDeviceCallbacks) { - m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); - } - - if (!found) { // If we have previously seen this device, don't record it again. - m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); - } - - break; - } // ESP_GAP_SEARCH_INQ_RES_EVT - - default: { - break; - } - } // switch - search_evt - - - break; - } // ESP_GAP_BLE_SCAN_RESULT_EVT - - default: { - break; - } // default - } // End switch -} // gapEventHandler - - -/** - * @brief Should we perform an active or passive scan? - * The default is a passive scan. An active scan means that we will wish a scan response. - * @param [in] active If true, we perform an active scan otherwise a passive scan. - * @return N/A. - */ -void BLEScan::setActiveScan(bool active) { - if (active) { - m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; - } else { - m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; - } -} // setActiveScan - - -/** - * @brief Set the call backs to be invoked. - * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. - * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. - */ -void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { - m_wantDuplicates = wantDuplicates; - m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; -} // setAdvertisedDeviceCallbacks - - -/** - * @brief Set the interval to scan. - * @param [in] The interval in msecs. - */ -void BLEScan::setInterval(uint16_t intervalMSecs) { - m_scan_params.scan_interval = intervalMSecs / 0.625; -} // setInterval - - -/** - * @brief Set the window to actively scan. - * @param [in] windowMSecs How long to actively scan. - */ -void BLEScan::setWindow(uint16_t windowMSecs) { - m_scan_params.scan_window = windowMSecs / 0.625; -} // setWindow - - -/** - * @brief Start scanning. - * @param [in] duration The duration in seconds for which to scan. - * @param [in] scanCompleteCB A function to be called when scanning has completed. - * @return True if scan started or false if there was an error. - */ -bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { - ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); - - m_semaphoreScanEnd.take(std::string("start")); - m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. - - m_scanResults.m_vectorAdvertisedDevices.clear(); - - esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - m_semaphoreScanEnd.give(); - return false; - } - - errRc = ::esp_ble_gap_start_scanning(duration); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - m_semaphoreScanEnd.give(); - return false; - } - - m_stopped = false; - - ESP_LOGD(LOG_TAG, "<< start()"); - return true; -} // start - - -/** - * @brief Start scanning and block until scanning has been completed. - * @param [in] duration The duration in seconds for which to scan. - * @return The BLEScanResults. - */ -BLEScanResults BLEScan::start(uint32_t duration) { - if(start(duration, nullptr)) { - m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. - } - return m_scanResults; -} // start - - -/** - * @brief Stop an in progress scan. - * @return N/A. - */ -void BLEScan::stop() { - ESP_LOGD(LOG_TAG, ">> stop()"); - - esp_err_t errRc = ::esp_ble_gap_stop_scanning(); - - m_stopped = true; - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreScanEnd.give(); - - ESP_LOGD(LOG_TAG, "<< stop()"); -} // stop - - -/** - * @brief Dump the scan results to the log. - */ -void BLEScanResults::dump() { - ESP_LOGD(LOG_TAG, ">> Dump scan results:"); - for (int i=0; i +#include + +#include + +#include "BLEAdvertisedDevice.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +static const char* LOG_TAG = "BLEScan"; + + +/** + * Constructor + */ +BLEScan::BLEScan() { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. + m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; + m_wantDuplicates = false; + setInterval(100); + setWindow(100); +} // BLEScan + + +/** + * @brief Handle GAP events related to scans. + * @param [in] event The event type for this event. + * @param [in] param Parameter data for this event. + */ +void BLEScan::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + switch(event) { + + // ESP_GAP_BLE_SCAN_RESULT_EVT + // --------------------------- + // scan_rst: + // esp_gap_search_evt_t search_evt + // esp_bd_addr_t bda + // esp_bt_dev_type_t dev_type + // esp_ble_addr_type_t ble_addr_type + // esp_ble_evt_type_t ble_evt_type + // int rssi + // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] + // int flag + // int num_resps + // uint8_t adv_data_len + // uint8_t scan_rsp_len + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + + switch(param->scan_rst.search_evt) { + // + // ESP_GAP_SEARCH_INQ_CMPL_EVT + // + // Event that indicates that the duration allowed for the search has completed or that we have been + // asked to stop. + case ESP_GAP_SEARCH_INQ_CMPL_EVT: { + m_stopped = true; + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } + m_semaphoreScanEnd.give(); + break; + } // ESP_GAP_SEARCH_INQ_CMPL_EVT + + // + // ESP_GAP_SEARCH_INQ_RES_EVT + // + // Result that has arrived back from a Scan inquiry. + case ESP_GAP_SEARCH_INQ_RES_EVT: { + if (m_stopped) { // If we are not scanning, nothing to do with the extra results. + break; + } + +// Examine our list of previously scanned addresses and, if we found this one already, +// ignore it. + BLEAddress advertisedAddress(param->scan_rst.bda); + bool found = false; + + for (int i=0; iscan_rst.rssi); + advertisedDevice.setAdFlag(param->scan_rst.flag); + advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); + advertisedDevice.setScan(this); + + if (m_pAdvertisedDeviceCallbacks) { + m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + } + + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + } + + break; + } // ESP_GAP_SEARCH_INQ_RES_EVT + + default: { + break; + } + } // switch - search_evt + + + break; + } // ESP_GAP_BLE_SCAN_RESULT_EVT + + default: { + break; + } // default + } // End switch +} // gapEventHandler + + +/** + * @brief Should we perform an active or passive scan? + * The default is a passive scan. An active scan means that we will wish a scan response. + * @param [in] active If true, we perform an active scan otherwise a passive scan. + * @return N/A. + */ +void BLEScan::setActiveScan(bool active) { + if (active) { + m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; + } else { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; + } +} // setActiveScan + + +/** + * @brief Set the call backs to be invoked. + * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + */ +void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { + m_wantDuplicates = wantDuplicates; + m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; +} // setAdvertisedDeviceCallbacks + + +/** + * @brief Set the interval to scan. + * @param [in] The interval in msecs. + */ +void BLEScan::setInterval(uint16_t intervalMSecs) { + m_scan_params.scan_interval = intervalMSecs / 0.625; +} // setInterval + + +/** + * @brief Set the window to actively scan. + * @param [in] windowMSecs How long to actively scan. + */ +void BLEScan::setWindow(uint16_t windowMSecs) { + m_scan_params.scan_window = windowMSecs / 0.625; +} // setWindow + + +/** + * @brief Start scanning. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @return True if scan started or false if there was an error. + */ +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { + ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + + m_semaphoreScanEnd.take(std::string("start")); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. + + m_scanResults.m_vectorAdvertisedDevices.clear(); + + esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + errRc = ::esp_ble_gap_start_scanning(duration); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + m_stopped = false; + + ESP_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +BLEScanResults BLEScan::start(uint32_t duration) { + if(start(duration, nullptr)) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } + return m_scanResults; +} // start + + +/** + * @brief Stop an in progress scan. + * @return N/A. + */ +void BLEScan::stop() { + ESP_LOGD(LOG_TAG, ">> stop()"); + + esp_err_t errRc = ::esp_ble_gap_stop_scanning(); + + m_stopped = true; + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreScanEnd.give(); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // stop + + +/** + * @brief Dump the scan results to the log. + */ +void BLEScanResults::dump() { + ESP_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; i - -#include -#include "BLEAdvertisedDevice.h" -#include "BLEClient.h" -#include "FreeRTOS.h" - -class BLEAdvertisedDevice; -class BLEAdvertisedDeviceCallbacks; -class BLEClient; -class BLEScan; - - -/** - * @brief The result of having performed a scan. - * When a scan completes, we have a set of found devices. Each device is described - * by a BLEAdvertisedDevice object. The number of items in the set is given by - * getCount(). We can retrieve a device by calling getDevice() passing in the - * index (starting at 0) of the desired device. - */ -class BLEScanResults { -public: - void dump(); - int getCount(); - BLEAdvertisedDevice getDevice(uint32_t i); - -private: - friend BLEScan; - std::vector m_vectorAdvertisedDevices; -}; - -/** - * @brief Perform and manage %BLE scans. - * - * Scanning is associated with a %BLE client that is attempting to locate BLE servers. - */ -class BLEScan { -public: - void setActiveScan(bool active); - void setAdvertisedDeviceCallbacks( - BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, - bool wantDuplicates = false); - void setInterval(uint16_t intervalMSecs); - void setWindow(uint16_t windowMSecs); - bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); - BLEScanResults start(uint32_t duration); - void stop(); - -private: - BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. - friend class BLEDevice; - void handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param); - void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); - - - esp_ble_scan_params_t m_scan_params; - BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; - bool m_stopped; - FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); - BLEScanResults m_scanResults; - bool m_wantDuplicates; - void (*m_scanCompleteCB)(BLEScanResults scanResults); -}; // BLEScan - -#endif /* CONFIG_BT_ENABLED */ -#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ +/* + * BLEScan.h + * + * Created on: Jul 1, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ +#define COMPONENTS_CPP_UTILS_BLESCAN_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +#include +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "FreeRTOS.h" + +class BLEAdvertisedDevice; +class BLEAdvertisedDeviceCallbacks; +class BLEClient; +class BLEScan; + + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ +class BLEScanResults { +public: + void dump(); + int getCount(); + BLEAdvertisedDevice getDevice(uint32_t i); + +private: + friend BLEScan; + std::vector m_vectorAdvertisedDevices; +}; + +/** + * @brief Perform and manage %BLE scans. + * + * Scanning is associated with a %BLE client that is attempting to locate BLE servers. + */ +class BLEScan { +public: + void setActiveScan(bool active); + void setAdvertisedDeviceCallbacks( + BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates = false); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); + BLEScanResults start(uint32_t duration); + void stop(); + +private: + BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. + friend class BLEDevice; + void handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); + + + esp_ble_scan_params_t m_scan_params; + BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; + bool m_stopped; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + BLEScanResults m_scanResults; + bool m_wantDuplicates; + void (*m_scanCompleteCB)(BLEScanResults scanResults); +}; // BLEScan + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index 2d52b015..e35a398a 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -1,71 +1,71 @@ -/* - * BLESecurity.h - * - * Created on: Dec 17, 2017 - * Author: chegewara - */ - -#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ -#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include - -class BLESecurity { -public: - BLESecurity(); - virtual ~BLESecurity(); - void setAuthenticationMode(esp_ble_auth_req_t auth_req); - void setCapability(esp_ble_io_cap_t iocap); - void setInitEncryptionKey(uint8_t init_key); - void setRespEncryptionKey(uint8_t resp_key); - void setKeySize(uint8_t key_size = 16); - static char* esp_key_type_to_str(esp_ble_key_type_t key_type); - -private: - esp_ble_auth_req_t m_authReq; - esp_ble_io_cap_t m_iocap; - uint8_t m_initKey; - uint8_t m_respKey; - uint8_t m_keySize; -}; // BLESecurity - - -/* - * @brief Callbacks to handle GAP events related to authorization - */ -class BLESecurityCallbacks { -public: - virtual ~BLESecurityCallbacks() {}; - - /** - * @brief Its request from peer device to input authentication pin code displayed on peer device. - * It requires that our device is capable to input 6-digits code by end user - * @return Return 6-digits integer value from input device - */ - virtual uint32_t onPassKeyRequest() = 0; - - /** - * @brief Provide us 6-digits code to perform authentication. - * It requires that our device is capable to display this code to end user - * @param - */ - virtual void onPassKeyNotify(uint32_t pass_key) = 0; - - /** - * @brief Here we can make decision if we want to let negotiate authorization with peer device or not - * return Return true if we accept this peer device request - */ - - virtual bool onSecurityRequest() = 0 ; - /** - * Provide us information when authentication process is completed - */ - virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; - - virtual bool onConfirmPIN(uint32_t pin) = 0; -}; // BLESecurityCallbacks - -#endif // CONFIG_BT_ENABLED -#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ +/* + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +class BLESecurity { +public: + BLESecurity(); + virtual ~BLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + static char* esp_key_type_to_str(esp_ble_key_type_t key_type); + +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; +}; // BLESecurity + + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class BLESecurityCallbacks { +public: + virtual ~BLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + + virtual bool onSecurityRequest() = 0 ; + /** + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 340ea560..863eb66f 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -1,435 +1,435 @@ -/* - * BLEService.cpp - * - * Created on: Mar 25, 2017 - * Author: kolban - */ - -// A service is identified by a UUID. A service is also the container for one or more characteristics. - -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include - -#include -#include -#include - -#include "BLEServer.h" -#include "BLEService.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" - -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -#define NULL_HANDLE (0xffff) - -static const char* LOG_TAG = "BLEService"; // Tag for logging. - -/** - * @brief Construct an instance of the BLEService - * @param [in] uuid The UUID of the service. - * @param [in] numHandles The maximum number of handles associated with the service. - */ -BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { -} - - -/** - * @brief Construct an instance of the BLEService - * @param [in] uuid The UUID of the service. - * @param [in] numHandles The maximum number of handles associated with the service. - */ -BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_pServer = nullptr; - //m_serializeMutex.setName("BLEService"); - m_lastCreatedCharacteristic = nullptr; - m_numHandles = numHandles; -} // BLEService - - -/** - * @brief Create the service. - * Create the service. - * @param [in] gatts_if The handle of the GATT server interface. - * @return N/A. - */ - -void BLEService::executeCreate(BLEServer *pServer) { - //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - //getUUID(); // Needed for a weird bug fix - //char x[1000]; - //memcpy(x, &m_uuid, sizeof(m_uuid)); - //char x[10]; - //memcpy(x, &deleteMe, 10); - m_pServer = pServer; - m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT - - esp_gatt_srvc_id_t srvc_id; - srvc_id.is_primary = true; - srvc_id.id.inst_id = m_id; - srvc_id.id.uuid = *m_uuid.getNative(); - esp_err_t errRc = ::esp_ble_gatts_create_service( - getServer()->getGattsIf(), - &srvc_id, - m_numHandles // The maximum number of handles associated with the service. - ); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreCreateEvt.wait("executeCreate"); - ESP_LOGD(LOG_TAG, "<< executeCreate"); -} // executeCreate - - -/** - * @brief Delete the service. - * Delete the service. - * @return N/A. - */ - -void BLEService::executeDelete() { - ESP_LOGD(LOG_TAG, ">> executeDelete()"); - m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT - - esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreDeleteEvt.wait("executeDelete"); - ESP_LOGD(LOG_TAG, "<< executeDelete"); -} // executeDelete - - -/** - * @brief Dump details of this BLE GATT service. - * @return N/A. - */ -void BLEService::dump() { - ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", - m_uuid.toString().c_str(), - m_handle); - ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); -} // dump - - -/** - * @brief Get the UUID of the service. - * @return the UUID of the service. - */ -BLEUUID BLEService::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Start the service. - * Here we wish to start the service which means that we will respond to partner requests about it. - * Starting a service also means that we can create the corresponding characteristics. - * @return Start the service. - */ -void BLEService::start() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// - ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); - return; - } - - - BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); - - while(pCharacteristic != nullptr) { - m_lastCreatedCharacteristic = pCharacteristic; - pCharacteristic->executeCreate(this); - - pCharacteristic = m_characteristicMap.getNext(); - } - // Start each of the characteristics ... these are found in the m_characteristicMap. - - m_semaphoreStartEvt.take("start"); - esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStartEvt.wait("start"); - - ESP_LOGD(LOG_TAG, "<< start()"); -} // start - - -/** - * @brief Stop the service. - * @return Stop the service. - */ -void BLEService::stop() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// - ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); - return; - } - - m_semaphoreStopEvt.take("stop"); - esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStopEvt.wait("stop"); - - ESP_LOGD(LOG_TAG, "<< stop()"); -} // start - - -/** - * @brief Set the handle associated with this service. - * @param [in] handle The handle associated with the service. - */ -void BLEService::setHandle(uint16_t handle) { - ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); - if (m_handle != NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); - return; - } - m_handle = handle; - ESP_LOGD(LOG_TAG, "<< setHandle"); -} // setHandle - - -/** - * @brief Get the handle associated with this service. - * @return The handle associated with this service. - */ -uint16_t BLEService::getHandle() { - return m_handle; -} // getHandle - - -/** - * @brief Add a characteristic to the service. - * @param [in] pCharacteristic A pointer to the characteristic to be added. - */ -void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { -// We maintain a mapping of characteristics owned by this service. These are managed by the -// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic -// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). -// - ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); - ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", - pCharacteristic->getUUID().toString().c_str(), - toString().c_str()); - - // Check that we don't add the same characteristic twice. - if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { - ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); - //return; - } - - // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID - // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. - m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); - - ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); -} // addCharacteristic - - -/** - * @brief Create a new BLE Characteristic associated with this service. - * @param [in] uuid - The UUID of the characteristic. - * @param [in] properties - The properties of the characteristic. - * @return The new BLE characteristic. - */ -BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { - return createCharacteristic(BLEUUID(uuid), properties); -} - - -/** - * @brief Create a new BLE Characteristic associated with this service. - * @param [in] uuid - The UUID of the characteristic. - * @param [in] properties - The properties of the characteristic. - * @return The new BLE characteristic. - */ -BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { - BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); - addCharacteristic(pCharacteristic); - return pCharacteristic; -} // createCharacteristic - - -/** - * @brief Handle a GATTS server event. - */ -void BLEService::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - - - switch(event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - - // If we have reached the correct service, then locate the characteristic and remember the handle - // for that characteristic. - case ESP_GATTS_ADD_CHAR_EVT: { - if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); - if (pCharacteristic == nullptr) { - ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", - BLEUUID(param->add_char.char_uuid).toString().c_str()); - dump(); - break; - } - pCharacteristic->setHandle(param->add_char.attr_handle); - m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); - //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); - break; - } // Reached the correct service. - break; - } // ESP_GATTS_ADD_CHAR_EVT - - - // ESP_GATTS_START_EVT - // - // start: - // esp_gatt_status_t status - // uint16_t service_handle - case ESP_GATTS_START_EVT: { - if (param->start.service_handle == getHandle()) { - m_semaphoreStartEvt.give(); - } - break; - } // ESP_GATTS_START_EVT - - // ESP_GATTS_STOP_EVT - // - // stop: - // esp_gatt_status_t status - // uint16_t service_handle - // - case ESP_GATTS_STOP_EVT: { - if (param->stop.service_handle == getHandle()) { - m_semaphoreStopEvt.give(); - } - break; - } // ESP_GATTS_STOP_EVT - - - // ESP_GATTS_CREATE_EVT - // Called when a new service is registered as having been created. - // - // create: - // * esp_gatt_status_t status - // * uint16_t service_handle - // * esp_gatt_srvc_id_t service_id - // * - esp_gatt_id id - // * - esp_bt_uuid uuid - // * - uint8_t inst_id - // * - bool is_primary - // - case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { - setHandle(param->create.service_handle); - m_semaphoreCreateEvt.give(); - } - break; - } // ESP_GATTS_CREATE_EVT - - - // ESP_GATTS_DELETE_EVT - // Called when a service is deleted. - // - // delete: - // * esp_gatt_status_t status - // * uint16_t service_handle - // - case ESP_GATTS_DELETE_EVT: { - if (param->del.service_handle == getHandle()) { - m_semaphoreDeleteEvt.give(); - } - break; - } // ESP_GATTS_DELETE_EVT - - default: { - break; - } // Default - } // Switch - - // Invoke the GATTS handler in each of the associated characteristics. - m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); -} // handleGATTServerEvent - - -BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { - return getCharacteristic(BLEUUID(uuid)); -} - - -BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { - return m_characteristicMap.getByUUID(uuid); -} - - -/** - * @brief Return a string representation of this service. - * A service is defined by: - * * Its UUID - * * Its handle - * @return A string representation of this service. - */ -std::string BLEService::toString() { - std::stringstream stringStream; - stringStream << "UUID: " << getUUID().toString() << - ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle(); - return stringStream.str(); -} // toString - - -/** - * @brief Get the last created characteristic. - * It is lamentable that this function has to exist. It returns the last created characteristic. - * We need this because the descriptor API is built around the notion that a new descriptor, when created, - * is associated with the last characteristics created and we need that information. - * @return The last created characteristic. - */ -BLECharacteristic* BLEService::getLastCreatedCharacteristic() { - return m_lastCreatedCharacteristic; -} // getLastCreatedCharacteristic - - -/** - * @brief Get the BLE server associated with this service. - * @return The BLEServer associated with this service. - */ -BLEServer* BLEService::getServer() { - return m_pServer; -} // getServer - -#endif // CONFIG_BT_ENABLED +/* + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include + +#include +#include +#include + +#include "BLEServer.h" +#include "BLEService.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" + +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +#define NULL_HANDLE (0xffff) + +static const char* LOG_TAG = "BLEService"; // Tag for logging. + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; + //m_serializeMutex.setName("BLEService"); + m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; +} // BLEService + + +/** + * @brief Create the service. + * Create the service. + * @param [in] gatts_if The handle of the GATT server interface. + * @return N/A. + */ + +void BLEService::executeCreate(BLEServer *pServer) { + //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); + //getUUID(); // Needed for a weird bug fix + //char x[1000]; + //memcpy(x, &m_uuid, sizeof(m_uuid)); + //char x[10]; + //memcpy(x, &deleteMe, 10); + m_pServer = pServer; + m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + + esp_gatt_srvc_id_t srvc_id; + srvc_id.is_primary = true; + srvc_id.id.inst_id = m_id; + srvc_id.id.uuid = *m_uuid.getNative(); + esp_err_t errRc = ::esp_ble_gatts_create_service( + getServer()->getGattsIf(), + &srvc_id, + m_numHandles // The maximum number of handles associated with the service. + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreCreateEvt.wait("executeCreate"); + ESP_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Delete the service. + * Delete the service. + * @return N/A. + */ + +void BLEService::executeDelete() { + ESP_LOGD(LOG_TAG, ">> executeDelete()"); + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT + + esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreDeleteEvt.wait("executeDelete"); + ESP_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void BLEService::dump() { + ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", + m_uuid.toString().c_str(), + m_handle); + ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +BLEUUID BLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Start the service. + * Here we wish to start the service which means that we will respond to partner requests about it. + * Starting a service also means that we can create the corresponding characteristics. + * @return Start the service. + */ +void BLEService::start() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); + return; + } + + + BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); + + while(pCharacteristic != nullptr) { + m_lastCreatedCharacteristic = pCharacteristic; + pCharacteristic->executeCreate(this); + + pCharacteristic = m_characteristicMap.getNext(); + } + // Start each of the characteristics ... these are found in the m_characteristicMap. + + m_semaphoreStartEvt.take("start"); + esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStartEvt.wait("start"); + + ESP_LOGD(LOG_TAG, "<< start()"); +} // start + + +/** + * @brief Stop the service. + * @return Stop the service. + */ +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start + + +/** + * @brief Set the handle associated with this service. + * @param [in] handle The handle associated with the service. + */ +void BLEService::setHandle(uint16_t handle) { + ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); + return; + } + m_handle = handle; + ESP_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t BLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Add a characteristic to the service. + * @param [in] pCharacteristic A pointer to the characteristic to be added. + */ +void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { +// We maintain a mapping of characteristics owned by this service. These are managed by the +// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic +// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). +// + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + pCharacteristic->getUUID().toString().c_str(), + toString().c_str()); + + // Check that we don't add the same characteristic twice. + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + //return; + } + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); + + ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); +} // addCharacteristic + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(BLEUUID(uuid), properties); +} + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { + BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); + addCharacteristic(pCharacteristic); + return pCharacteristic; +} // createCharacteristic + + +/** + * @brief Handle a GATTS server event. + */ +void BLEService::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) { + + + switch(event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + + // If we have reached the correct service, then locate the characteristic and remember the handle + // for that characteristic. + case ESP_GATTS_ADD_CHAR_EVT: { + if (m_handle == param->add_char.service_handle) { + BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); + if (pCharacteristic == nullptr) { + ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", + BLEUUID(param->add_char.char_uuid).toString().c_str()); + dump(); + break; + } + pCharacteristic->setHandle(param->add_char.attr_handle); + m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); + //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); + break; + } // Reached the correct service. + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_START_EVT + // + // start: + // esp_gatt_status_t status + // uint16_t service_handle + case ESP_GATTS_START_EVT: { + if (param->start.service_handle == getHandle()) { + m_semaphoreStartEvt.give(); + } + break; + } // ESP_GATTS_START_EVT + + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } + break; + } // ESP_GATTS_STOP_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // * - esp_gatt_id id + // * - esp_bt_uuid uuid + // * - uint8_t inst_id + // * - bool is_primary + // + case ESP_GATTS_CREATE_EVT: { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { + setHandle(param->create.service_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // + case ESP_GATTS_DELETE_EVT: { + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } + break; + } // ESP_GATTS_DELETE_EVT + + default: { + break; + } // Default + } // Switch + + // Invoke the GATTS handler in each of the associated characteristics. + m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); +} // handleGATTServerEvent + + +BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(BLEUUID(uuid)); +} + + +BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { + return m_characteristicMap.getByUUID(uuid); +} + + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string BLEService::toString() { + std::stringstream stringStream; + stringStream << "UUID: " << getUUID().toString() << + ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle(); + return stringStream.str(); +} // toString + + +/** + * @brief Get the last created characteristic. + * It is lamentable that this function has to exist. It returns the last created characteristic. + * We need this because the descriptor API is built around the notion that a new descriptor, when created, + * is associated with the last characteristics created and we need that information. + * @return The last created characteristic. + */ +BLECharacteristic* BLEService::getLastCreatedCharacteristic() { + return m_lastCreatedCharacteristic; +} // getLastCreatedCharacteristic + + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +BLEServer* BLEService::getServer() { + return m_pServer; +} // getServer + +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 93b4b2c6..d2ab0759 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -1,104 +1,104 @@ -/* - * BLEService.h - * - * Created on: Mar 25, 2017 - * Author: kolban - */ - -#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#define COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include - -#include "BLECharacteristic.h" -#include "BLEServer.h" -#include "BLEUUID.h" -#include "FreeRTOS.h" - -class BLEServer; - -/** - * @brief A data mapping used to manage the set of %BLE characteristics known to the server. - */ -class BLECharacteristicMap { -public: - void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); - void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); - void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); - BLECharacteristic* getByUUID(BLEUUID uuid); - BLECharacteristic* getByHandle(uint16_t handle); - BLECharacteristic* getFirst(); - BLECharacteristic* getNext(); - std::string toString(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - - -private: - std::map m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; -}; - - -/** - * @brief The model of a %BLE service. - * - */ -class BLEService { -public: - void addCharacteristic(BLECharacteristic* pCharacteristic); - BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); - BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); - void dump(); - void executeCreate(BLEServer* pServer); - void executeDelete(); - BLECharacteristic* getCharacteristic(const char* uuid); - BLECharacteristic* getCharacteristic(BLEUUID uuid); - BLEUUID getUUID(); - BLEServer* getServer(); - void start(); - void stop(); - std::string toString(); - uint16_t getHandle(); - uint8_t m_id = 0; - -private: - BLEService(const char* uuid, uint32_t numHandles); - BLEService(BLEUUID uuid, uint32_t numHandles); - friend class BLEServer; - friend class BLEServiceMap; - friend class BLEDescriptor; - friend class BLECharacteristic; - friend class BLEDevice; - - BLECharacteristicMap m_characteristicMap; - uint16_t m_handle; - BLECharacteristic* m_lastCreatedCharacteristic; - BLEServer* m_pServer; - BLEUUID m_uuid; - - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); - FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); - FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); - - uint32_t m_numHandles; - - BLECharacteristic* getLastCreatedCharacteristic(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - void setHandle(uint16_t handle); - //void setService(esp_gatt_srvc_id_t srvc_id); -}; // BLEService - - -#endif // CONFIG_BT_ENABLED -#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ +/* + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#define COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include "BLECharacteristic.h" +#include "BLEServer.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLEServer; + +/** + * @brief A data mapping used to manage the set of %BLE characteristics known to the server. + */ +class BLECharacteristicMap { +public: + void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); + void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); + BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(BLEUUID uuid); + BLECharacteristic* getByHandle(uint16_t handle); + BLECharacteristic* getFirst(); + BLECharacteristic* getNext(); + std::string toString(); + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + + +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE service. + * + */ +class BLEService { +public: + void addCharacteristic(BLECharacteristic* pCharacteristic); + BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); + BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); + void dump(); + void executeCreate(BLEServer* pServer); + void executeDelete(); + BLECharacteristic* getCharacteristic(const char* uuid); + BLECharacteristic* getCharacteristic(BLEUUID uuid); + BLEUUID getUUID(); + BLEServer* getServer(); + void start(); + void stop(); + std::string toString(); + uint16_t getHandle(); + uint8_t m_id = 0; + +private: + BLEService(const char* uuid, uint32_t numHandles); + BLEService(BLEUUID uuid, uint32_t numHandles); + friend class BLEServer; + friend class BLEServiceMap; + friend class BLEDescriptor; + friend class BLECharacteristic; + friend class BLEDevice; + + BLECharacteristicMap m_characteristicMap; + uint16_t m_handle; + BLECharacteristic* m_lastCreatedCharacteristic; + BLEServer* m_pServer; + BLEUUID m_uuid; + + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); + + uint32_t m_numHandles; + + BLECharacteristic* getLastCreatedCharacteristic(); + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + void setHandle(uint16_t handle); + //void setService(esp_gatt_srvc_id_t srvc_id); +}; // BLEService + + +#endif // CONFIG_BT_ENABLED +#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index dd828fae..179de7a8 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -1,132 +1,132 @@ -/* - * BLEServiceMap.cpp - * - * Created on: Jun 22, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include "BLEService.h" - - -/** - * @brief Return the service by UUID. - * @param [in] UUID The UUID to look up the service. - * @return The characteristic. - */ -BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); -} - -/** - * @brief Return the service by UUID. - * @param [in] UUID The UUID to look up the service. - * @return The characteristic. - */ -BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { - for (auto &myPair : m_uuidMap) { - if (myPair.first->getUUID().equals(uuid)) { - return myPair.first; - } - } - //return m_uuidMap.at(uuid.toString()); - return nullptr; -} // getByUUID - - -/** - * @brief Return the service by handle. - * @param [in] handle The handle to look up the service. - * @return The service. - */ -BLEService* BLEServiceMap::getByHandle(uint16_t handle) { - return m_handleMap.at(handle); -} // getByHandle - - -/** - * @brief Set the service by UUID. - * @param [in] uuid The uuid of the service. - * @param [in] characteristic The service to cache. - * @return N/A. - */ -void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService *service) { - m_uuidMap.insert(std::pair(service, uuid.toString())); -} // setByUUID - - -/** - * @brief Set the service by handle. - * @param [in] handle The handle of the service. - * @param [in] service The service to cache. - * @return N/A. - */ -void BLEServiceMap::setByHandle(uint16_t handle, - BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); -} // setByHandle - - -/** - * @brief Return a string representation of the service map. - * @return A string representation of the service map. - */ -std::string BLEServiceMap::toString() { - std::stringstream stringStream; - stringStream << std::hex << std::setfill('0'); - for (auto &myPair: m_handleMap) { - stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; - } - return stringStream.str(); -} // toString - -void BLEServiceMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { - myPair.first->handleGATTServerEvent(event, gatts_if, param); - } -} - -/** - * @brief Get the first service in the map. - * @return The first service in the map. - */ -BLEService* BLEServiceMap::getFirst() { - m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEService* pRet = m_iterator->first; - m_iterator++; - return pRet; -} // getFirst - -/** - * @brief Get the next service in the map. - * @return The next service in the map. - */ -BLEService* BLEServiceMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEService* pRet = m_iterator->first; - m_iterator++; - return pRet; -} // getNext - -/** - * @brief Removes service from maps. - * @return N/A. - */ -void BLEServiceMap::removeService(BLEService *service){ - m_handleMap.erase(service->getHandle()); - m_uuidMap.erase(service); -} // removeService - -#endif /* CONFIG_BT_ENABLED */ +/* + * BLEServiceMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEService.h" + + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(const char* uuid) { + return getByUUID(BLEUUID(uuid)); +} + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the service by handle. + * @param [in] handle The handle to look up the service. + * @return The service. + */ +BLEService* BLEServiceMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Set the service by UUID. + * @param [in] uuid The uuid of the service. + * @param [in] characteristic The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByUUID(BLEUUID uuid, + BLEService *service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the service by handle. + * @param [in] handle The handle of the service. + * @param [in] service The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByHandle(uint16_t handle, + BLEService* service) { + m_handleMap.insert(std::pair(handle, service)); +} // setByHandle + + +/** + * @brief Return a string representation of the service map. + * @return A string representation of the service map. + */ +std::string BLEServiceMap::toString() { + std::stringstream stringStream; + stringStream << std::hex << std::setfill('0'); + for (auto &myPair: m_handleMap) { + stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; + } + return stringStream.str(); +} // toString + +void BLEServiceMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +BLEService* BLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +BLEService* BLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void BLEServiceMap::removeService(BLEService *service){ + m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 09d59d55..81d37c36 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -1,904 +1,904 @@ -/* - * WiFi.cpp - * - * Created on: Feb 25, 2017 - * Author: kolban - */ - -//#define _GLIBCXX_USE_C99 -#include -#include -#include -#include -#include "sdkconfig.h" - - -#include "WiFi.h" -#include -#include -#include -#include -#include -#include "GeneralUtils.h" -#include -#include -#include -#include -#include - -#include - - -static const char* LOG_TAG = "WiFi"; - - -/* -static void setDNSServer(char *ip) { - ip_addr_t dnsserver; - ESP_LOGD(tag, "Setting DNS[%d] to %s", 0, ip); - inet_pton(AF_INET, ip, &dnsserver); - ESP_LOGD(tag, "ip of DNS is %.8x", *(uint32_t *)&dnsserver); - dns_setserver(0, &dnsserver); -} -*/ - - -/** - * @brief Creates and uses a default event handler - */ -WiFi::WiFi() - : ip(0) - , gw(0) - , netmask(0) - , m_pWifiEventHandler(nullptr) -{ - m_eventLoopStarted = false; - m_initCalled = false; - //m_pWifiEventHandler = new WiFiEventHandler(); - m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? -} // WiFi - - -/** - * @brief Deletes the event handler that was used by the class - */ -WiFi::~WiFi() { - if (m_pWifiEventHandler != nullptr) { - delete m_pWifiEventHandler; - m_pWifiEventHandler = nullptr; - } -} // ~WiFi - - -/** - * @brief Add a reference to a DNS server. - * - * Here we define a server that will act as a DNS server. We can add two DNS - * servers in total. The first will be the primary, the second will be the backup. - * The public Google DNS servers are "8.8.8.8" and "8.8.4.4". - * - * For example: - * - * @code{.cpp} - * wifi.addDNSServer("8.8.8.8"); - * wifi.addDNSServer("8.8.4.4"); - * @endcode - * - * @param [in] ip The IP address of the DNS Server. - * @return N/A. - */ -void WiFi::addDNSServer(const std::string& ip) { - addDNSServer(ip.c_str()); -} // addDNSServer - - -void WiFi::addDNSServer(const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { - addDNSServer(ip); - } -} // addDNSServer - - -void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - init(); - ::dns_setserver(m_dnsCount, &ip); - m_dnsCount++; - m_dnsCount %= 2; -} // addDNSServer - - -/** - * @brief Set a reference to a DNS server. - * - * Here we define a server that will act as a DNS server. We use numdns to specify which DNS server to set - * - * For example: - * - * @code{.cpp} - * wifi.setDNSServer(0, "8.8.8.8"); - * wifi.setDNSServer(1, "8.8.4.4"); - * @endcode - * - * @param [in] numdns The DNS number we wish to set - * @param [in] ip The IP address of the DNS Server. - * @return N/A. - */ -void WiFi::setDNSServer(int numdns, const std::string& ip) { - setDNSServer(numdns, ip.c_str()); -} // setDNSServer - - -void WiFi::setDNSServer(int numdns, const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { - setDNSServer(numdns, dns_server); - } -} // setDNSServer - - -void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - init(); - ::dns_setserver(numdns, &ip); -} // setDNSServer - - -/** - * @brief Connect to an external access point. - * - * The event handler will be called back with the outcome of the connection. - * - * @param [in] ssid The network SSID of the access point to which we wish to connect. - * @param [in] password The password of the access point to which we wish to connect. - * @param [in] waitForConnection Block until the connection has an outcome. - * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect - * @return ESP_OK if we are now connected and wifi_err_reason_t if not. - */ -uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ - ESP_LOGD(LOG_TAG, ">> connectAP"); - - m_apConnectionStatus = UINT8_MAX; - init(); - - if (ip != 0 && gw != 0 && netmask != 0) { - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client - - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; - - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } - - esp_err_t errRc = ::esp_wifi_set_mode(mode); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - wifi_config_t sta_config; - ::memset(&sta_config, 0, sizeof(sta_config)); - ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); - ::memcpy(sta_config.sta.password, password.data(), password.size()); - sta_config.sta.bssid_set = 0; - errRc = ::esp_wifi_set_config(WIFI_IF_STA, &sta_config); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. - do { - ESP_LOGD(LOG_TAG, "esp_wifi_connect"); - errRc = ::esp_wifi_connect(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - } - while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s - m_connectFinished.give(); - - ESP_LOGD(LOG_TAG, "<< connectAP"); - return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. -} // connectAP - - - -/** - * @brief Dump diagnostics to the log. - */ -void WiFi::dump() { - ESP_LOGD(LOG_TAG, "WiFi Dump"); - ESP_LOGD(LOG_TAG, "---------"); - char ipAddrStr[30]; - ip_addr_t ip = ::dns_getserver(0); - inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); - ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); -} // dump - - -/** - * @brief Returns whether wifi is connected to an access point - */ -bool WiFi::isConnectedToAP() { - return m_apConnectionStatus; -} // isConnected - - - -/** - * @brief Primary event handler interface. - */ -/* STATIC */ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { - // This is the common event handler that we have provided for all event processing. It is called for every event - // that is received by the WiFi subsystem. The "ctx" parameter is an instance of the current WiFi object that we are - // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this - // an indirection vector to the real caller. - - WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. - - // Invoke the event handler. - esp_err_t rc; - if (pWiFi->m_pWifiEventHandler != nullptr) { - rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); - } else { - rc = ESP_OK; - } - - // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that - // indicates we are waiting for a connection complete. - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. - pWiFi->m_apConnectionStatus = ESP_OK; - } else { - pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; - } - pWiFi->m_connectFinished.give(); - } - - return rc; -} // eventHandler - - -/** - * @brief Get the AP IP Info. - * @return The AP IP Info. - */ -tcpip_adapter_ip_info_t WiFi::getApIpInfo() { - //init(); - tcpip_adapter_ip_info_t ipInfo; - ::tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); - return ipInfo; -} // getApIpInfo - - -/** - * @brief Get the MAC address of the AP interface. - * @return The MAC address of the AP interface. - */ -std::string WiFi::getApMac() { - uint8_t mac[6]; - //init(); - esp_wifi_get_mac(WIFI_IF_AP, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); -} // getApMac - - -/** - * @brief Get the AP SSID. - * @return The AP SSID. - */ -std::string WiFi::getApSSID() { - wifi_config_t conf; - //init(); - esp_wifi_get_config(WIFI_IF_AP, &conf); - return std::string((char *)conf.sta.ssid); -} // getApSSID - -/** - * @brief Get the current ESP32 IP form AP. - * @return The ESP32 IP. - */ -std::string WiFi::getApIp(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaIp - -/** - * @brief Get the current AP netmask. - * @return The Netmask IP. - */ -std::string WiFi::getApNetmask(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaNetmask - -/** - * @brief Get the current AP Gateway IP. - * @return The Gateway IP. - */ -std::string WiFi::getApGateway(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaGateway - -/** - * @brief Lookup an IP address by host name. - * - * @param [in] hostName The hostname to resolve. - * - * @return The IP address of the host or 0.0.0.0 if not found. - */ -struct in_addr WiFi::getHostByName(const std::string& hostName) { - return getHostByName(hostName.c_str()); -} // getHostByName - - -struct in_addr WiFi::getHostByName(const char* hostName) { - struct in_addr retAddr; - struct hostent *he = gethostbyname(hostName); - if (he == nullptr) { - retAddr.s_addr = 0; - ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); - } else { - retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); - } - return retAddr; -} // getHostByName - - -/** - * @brief Get the WiFi Mode. - * @return The WiFi Mode. - */ -std::string WiFi::getMode() { - wifi_mode_t mode; - esp_wifi_get_mode(&mode); - switch(mode) { - case WIFI_MODE_NULL: - return "WIFI_MODE_NULL"; - case WIFI_MODE_STA: - return "WIFI_MODE_STA"; - case WIFI_MODE_AP: - return "WIFI_MODE_AP"; - case WIFI_MODE_APSTA: - return "WIFI_MODE_APSTA"; - default: - return "unknown"; - } -} // getMode - - -/** - * @brief Get the STA IP Info. - * @return The STA IP Info. - */ -tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { - tcpip_adapter_ip_info_t ipInfo; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - return ipInfo; -} // getStaIpInfo - -/** - * @brief Get the current ESP32 IP form STA. - * @return The ESP32 IP. - */ -std::string WiFi::getStaIp(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaIp - - -/** - * @brief Get the current STA netmask. - * @return The Netmask IP. - */ -std::string WiFi::getStaNetmask(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaNetmask - - -/** - * @brief Get the current STA Gateway IP. - * @return The Gateway IP. - */ -std::string WiFi::getStaGateway(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaGateway - -/** - * @brief Get the MAC address of the STA interface. - * @return The MAC address of the STA interface. - */ -std::string WiFi::getStaMac() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); -} // getStaMac - - -/** - * @brief Get the STA SSID. - * @return The STA SSID. - */ -std::string WiFi::getStaSSID() { - wifi_config_t conf; - esp_wifi_get_config(WIFI_IF_STA, &conf); - return std::string((char *)conf.ap.ssid); -} // getStaSSID - - -/** - * @brief Initialize WiFi. - */ -/* PRIVATE */ void WiFi::init() { - - // If we have already started the event loop, then change the handler otherwise - // start the event loop. - if (m_eventLoopStarted) { - esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. - } else { - esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - m_eventLoopStarted = true; - } - // Now, one way or another, the event handler is WiFi::eventHandler. - - if (!m_initCalled) { - ::nvs_flash_init(); - ::tcpip_adapter_init(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_err_t errRc = ::esp_wifi_init(&cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - } - m_initCalled = true; -} // init - - -/** - * @brief Perform a WiFi scan looking for access points. - * - * An access point scan is performed and a vector of WiFi access point records - * is built and returned with one record per found scan instance. The scan is - * performed in a blocking fashion and will not return until the set of scanned - * access points has been built. - * - * @return A vector of WiFiAPRecord instances. - */ -std::vector WiFi::scan() { - ESP_LOGD(LOG_TAG, ">> scan"); - std::vector apRecords; - - init(); - - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - wifi_scan_config_t conf; - memset(&conf, 0, sizeof(conf)); - conf.show_hidden = true; - - esp_err_t rc = ::esp_wifi_scan_start(&conf, true); - if (rc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); - return apRecords; - } - - uint16_t apCount; // Number of access points available. - rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); - - wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); - if (list == nullptr) { - ESP_LOGE(LOG_TAG, "Failed to allocate memory"); - return apRecords; - } - - errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - for (auto i=0; i rhs.m_rssi;}); - return apRecords; -} // scan - - -/** - * @brief Start being an access point. - * - * @param[in] ssid The SSID to use to advertize for stations. - * @param[in] password The password to use for station connections. - * @param[in] auth The authorization mode for access to this access point. Options are: - * * WIFI_AUTH_OPEN - * * WIFI_AUTH_WPA_PSK - * * WIFI_AUTH_WPA2_PSK - * * WIFI_AUTH_WPA_WPA2_PSK - * * WIFI_AUTH_WPA2_ENTERPRISE - * * WIFI_AUTH_WEP - * @return N/A. - */ -void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { - startAP(ssid, password, auth, 0, false, 4); -} // startAP - -/** - * @brief Start being an access point. - * - * @param[in] ssid The SSID to use to advertize for stations. - * @param[in] password The password to use for station connections. - * @param[in] auth The authorization mode for access to this access point. Options are: - * * WIFI_AUTH_OPEN - * * WIFI_AUTH_WPA_PSK - * * WIFI_AUTH_WPA2_PSK - * * WIFI_AUTH_WPA_WPA2_PSK - * * WIFI_AUTH_WPA2_ENTERPRISE - * * WIFI_AUTH_WEP - * @param[in] channel from the access point. - * @param[in] is the ssid hidden, ore not. - * @param[in] limiting number of clients. - * @return N/A. - */ -void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { - ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); - - init(); - - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - // Build the apConfig structure. - wifi_config_t apConfig; - ::memset(&apConfig, 0, sizeof(apConfig)); - ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); - apConfig.ap.ssid_len = ssid.size(); - ::memcpy(apConfig.ap.password, password.data(), password.size()); - apConfig.ap.channel = channel; - apConfig.ap.authmode = auth; - apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; - apConfig.ap.max_connection = max_connection; - apConfig.ap.beacon_interval = 100; - - errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - - ESP_LOGD(LOG_TAG, "<< startAP"); -} // startAP - -/** - * @brief Set the event handler to use to process detected events. - * @param[in] wifiEventHandler The class that will be used to process events. - */ -void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { - ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); - this->m_pWifiEventHandler = wifiEventHandler; - ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); -} // setWifiEventHandler - - -/** - * @brief Set the IP info and enable DHCP if ip != 0. If called with ip == 0 then DHCP is enabled. - * If called with bad values it will do nothing. - * - * Do not call this method if we are being an access point ourselves. - * - * For example, prior to calling `connectAP()` we could invoke: - * - * @code{.cpp} - * myWifi.setIPInfo("192.168.1.99", "192.168.1.1", "255.255.255.0"); - * @endcode - * - * @param [in] ip IP address value. - * @param [in] gw Gateway value. - * @param [in] netmask Netmask value. - * @return N/A. - */ -void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask) { - setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); -} // setIPInfo - - - -void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { - uint32_t new_ip; - uint32_t new_gw; - uint32_t new_netmask; - - auto success = (bool)inet_pton(AF_INET, ip, &new_ip); - success = success && inet_pton(AF_INET, gw, &new_gw); - success = success && inet_pton(AF_INET, netmask, &new_netmask); - - if(!success) { - return; - } - - setIPInfo(new_ip, new_gw, new_netmask); -} // setIPInfo - - -/** - * @brief Set the IP Info based on the IP address, gateway and netmask. - * @param [in] ip The IP address of our ESP32. - * @param [in] gw The gateway we should use. - * @param [in] netmask Our TCP/IP netmask value. - */ -void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { - init(); - - this->ip = ip; - this->gw = gw; - this->netmask = netmask; - - if(ip != 0 && gw != 0 && netmask != 0) { - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } else { - ip = 0; - ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); - } -} // setIPInfo - - -/** - * @brief Return a string representation of the WiFi access point record. - * - * @return A string representation of the WiFi access point record. - */ -std::string WiFiAPRecord::toString() { - std::string auth; - switch(getAuthMode()) { - case WIFI_AUTH_OPEN: - auth = "WIFI_AUTH_OPEN"; - break; - case WIFI_AUTH_WEP: - auth = "WIFI_AUTH_WEP"; - break; - case WIFI_AUTH_WPA_PSK: - auth = "WIFI_AUTH_WPA_PSK"; - break; - case WIFI_AUTH_WPA2_PSK: - auth = "WIFI_AUTH_WPA2_PSK"; - break; - case WIFI_AUTH_WPA_WPA2_PSK: - auth = "WIFI_AUTH_WPA_WPA2_PSK"; - break; - default: - auth = ""; - break; - } -// std::stringstream s; -// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; - auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); - sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); - return std::string(std::move(info_str)); -} // toString - -/* -MDNS::MDNS() { - esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} - -MDNS::~MDNS() { - if (m_mdns_server != nullptr) { - mdns_free(m_mdns_server); - } - m_mdns_server = nullptr; -} -*/ - -/** - * @brief Define the service for mDNS. - * - * @param [in] service - * @param [in] proto - * @param [in] port - * @return N/A. - */ -/* -void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { - serviceAdd(service.c_str(), proto.c_str(), port); -} // serviceAdd - - -void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { - serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); -} // serviceInstanceSet - - -void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { - servicePortSet(service.c_str(), proto.c_str(), port); -} // servicePortSet - - -void MDNS::serviceRemove(const std::string& service, const std::string& proto) { - serviceRemove(service.c_str(), proto.c_str()); -} // serviceRemove -*/ - -/** - * @brief Set the mDNS hostname. - * - * @param [in] hostname The host name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setHostname(const std::string& hostname) { - setHostname(hostname.c_str()); -} // setHostname -*/ - -/** - * @brief Set the mDNS instance. - * - * @param [in] instance The instance name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setInstance(const std::string& instance) { - setInstance(instance.c_str()); -} // setInstance -*/ - -/** - * @brief Define the service for mDNS. - * - * @param [in] service - * @param [in] proto - * @param [in] port - * @return N/A. - */ -/* -void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { - esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_add: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceAdd - - -void MDNS::serviceInstanceSet(const char* service, const char* proto, const char* instance) { - esp_err_t errRc = ::mdns_service_instance_set(m_mdns_server, service, proto, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_instance_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceInstanceSet - - -void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) { - esp_err_t errRc = ::mdns_service_port_set(m_mdns_server, service, proto, port); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_port_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // servicePortSet - - -void MDNS::serviceRemove(const char* service, const char* proto) { - esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceRemove - -*/ -/** - * @brief Set the mDNS hostname. - * - * @param [in] hostname The host name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setHostname(const char* hostname) { - esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // setHostname -*/ - -/** - * @brief Set the mDNS instance. - * - * @param [in] instance The instance name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setInstance(const char* instance) { - esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // setInstance -*/ +/* + * WiFi.cpp + * + * Created on: Feb 25, 2017 + * Author: kolban + */ + +//#define _GLIBCXX_USE_C99 +#include +#include +#include +#include +#include "sdkconfig.h" + + +#include "WiFi.h" +#include +#include +#include +#include +#include +#include "GeneralUtils.h" +#include +#include +#include +#include +#include + +#include + + +static const char* LOG_TAG = "WiFi"; + + +/* +static void setDNSServer(char *ip) { + ip_addr_t dnsserver; + ESP_LOGD(tag, "Setting DNS[%d] to %s", 0, ip); + inet_pton(AF_INET, ip, &dnsserver); + ESP_LOGD(tag, "ip of DNS is %.8x", *(uint32_t *)&dnsserver); + dns_setserver(0, &dnsserver); +} +*/ + + +/** + * @brief Creates and uses a default event handler + */ +WiFi::WiFi() + : ip(0) + , gw(0) + , netmask(0) + , m_pWifiEventHandler(nullptr) +{ + m_eventLoopStarted = false; + m_initCalled = false; + //m_pWifiEventHandler = new WiFiEventHandler(); + m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? +} // WiFi + + +/** + * @brief Deletes the event handler that was used by the class + */ +WiFi::~WiFi() { + if (m_pWifiEventHandler != nullptr) { + delete m_pWifiEventHandler; + m_pWifiEventHandler = nullptr; + } +} // ~WiFi + + +/** + * @brief Add a reference to a DNS server. + * + * Here we define a server that will act as a DNS server. We can add two DNS + * servers in total. The first will be the primary, the second will be the backup. + * The public Google DNS servers are "8.8.8.8" and "8.8.4.4". + * + * For example: + * + * @code{.cpp} + * wifi.addDNSServer("8.8.8.8"); + * wifi.addDNSServer("8.8.4.4"); + * @endcode + * + * @param [in] ip The IP address of the DNS Server. + * @return N/A. + */ +void WiFi::addDNSServer(const std::string& ip) { + addDNSServer(ip.c_str()); +} // addDNSServer + + +void WiFi::addDNSServer(const char* ip) { + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + addDNSServer(ip); + } +} // addDNSServer + + +void WiFi::addDNSServer(ip_addr_t ip) { + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); + ::dns_setserver(m_dnsCount, &ip); + m_dnsCount++; + m_dnsCount %= 2; +} // addDNSServer + + +/** + * @brief Set a reference to a DNS server. + * + * Here we define a server that will act as a DNS server. We use numdns to specify which DNS server to set + * + * For example: + * + * @code{.cpp} + * wifi.setDNSServer(0, "8.8.8.8"); + * wifi.setDNSServer(1, "8.8.4.4"); + * @endcode + * + * @param [in] numdns The DNS number we wish to set + * @param [in] ip The IP address of the DNS Server. + * @return N/A. + */ +void WiFi::setDNSServer(int numdns, const std::string& ip) { + setDNSServer(numdns, ip.c_str()); +} // setDNSServer + + +void WiFi::setDNSServer(int numdns, const char* ip) { + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + setDNSServer(numdns, dns_server); + } +} // setDNSServer + + +void WiFi::setDNSServer(int numdns, ip_addr_t ip) { + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); + ::dns_setserver(numdns, &ip); +} // setDNSServer + + +/** + * @brief Connect to an external access point. + * + * The event handler will be called back with the outcome of the connection. + * + * @param [in] ssid The network SSID of the access point to which we wish to connect. + * @param [in] password The password of the access point to which we wish to connect. + * @param [in] waitForConnection Block until the connection has an outcome. + * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect + * @return ESP_OK if we are now connected and wifi_err_reason_t if not. + */ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ + ESP_LOGD(LOG_TAG, ">> connectAP"); + + m_apConnectionStatus = UINT8_MAX; + init(); + + if (ip != 0 && gw != 0 && netmask != 0) { + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client + + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } + + esp_err_t errRc = ::esp_wifi_set_mode(mode); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + wifi_config_t sta_config; + ::memset(&sta_config, 0, sizeof(sta_config)); + ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); + ::memcpy(sta_config.sta.password, password.data(), password.size()); + sta_config.sta.bssid_set = 0; + errRc = ::esp_wifi_set_config(WIFI_IF_STA, &sta_config); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. + do { + ESP_LOGD(LOG_TAG, "esp_wifi_connect"); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s + m_connectFinished.give(); + + ESP_LOGD(LOG_TAG, "<< connectAP"); + return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. +} // connectAP + + + +/** + * @brief Dump diagnostics to the log. + */ +void WiFi::dump() { + ESP_LOGD(LOG_TAG, "WiFi Dump"); + ESP_LOGD(LOG_TAG, "---------"); + char ipAddrStr[30]; + ip_addr_t ip = ::dns_getserver(0); + inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); + ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); +} // dump + + +/** + * @brief Returns whether wifi is connected to an access point + */ +bool WiFi::isConnectedToAP() { + return m_apConnectionStatus; +} // isConnected + + + +/** + * @brief Primary event handler interface. + */ +/* STATIC */ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { + // This is the common event handler that we have provided for all event processing. It is called for every event + // that is received by the WiFi subsystem. The "ctx" parameter is an instance of the current WiFi object that we are + // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this + // an indirection vector to the real caller. + + WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. + + // Invoke the event handler. + esp_err_t rc; + if (pWiFi->m_pWifiEventHandler != nullptr) { + rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + } else { + rc = ESP_OK; + } + + // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that + // indicates we are waiting for a connection complete. + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { + + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. + pWiFi->m_apConnectionStatus = ESP_OK; + } else { + pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; + } + pWiFi->m_connectFinished.give(); + } + + return rc; +} // eventHandler + + +/** + * @brief Get the AP IP Info. + * @return The AP IP Info. + */ +tcpip_adapter_ip_info_t WiFi::getApIpInfo() { + //init(); + tcpip_adapter_ip_info_t ipInfo; + ::tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); + return ipInfo; +} // getApIpInfo + + +/** + * @brief Get the MAC address of the AP interface. + * @return The MAC address of the AP interface. + */ +std::string WiFi::getApMac() { + uint8_t mac[6]; + //init(); + esp_wifi_get_mac(WIFI_IF_AP, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); +} // getApMac + + +/** + * @brief Get the AP SSID. + * @return The AP SSID. + */ +std::string WiFi::getApSSID() { + wifi_config_t conf; + //init(); + esp_wifi_get_config(WIFI_IF_AP, &conf); + return std::string((char *)conf.sta.ssid); +} // getApSSID + +/** + * @brief Get the current ESP32 IP form AP. + * @return The ESP32 IP. + */ +std::string WiFi::getApIp(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + +/** + * @brief Get the current AP netmask. + * @return The Netmask IP. + */ +std::string WiFi::getApNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + +/** + * @brief Get the current AP Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getApGateway(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + +/** + * @brief Lookup an IP address by host name. + * + * @param [in] hostName The hostname to resolve. + * + * @return The IP address of the host or 0.0.0.0 if not found. + */ +struct in_addr WiFi::getHostByName(const std::string& hostName) { + return getHostByName(hostName.c_str()); +} // getHostByName + + +struct in_addr WiFi::getHostByName(const char* hostName) { + struct in_addr retAddr; + struct hostent *he = gethostbyname(hostName); + if (he == nullptr) { + retAddr.s_addr = 0; + ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); + } else { + retAddr = *(struct in_addr *)(he->h_addr_list[0]); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + } + return retAddr; +} // getHostByName + + +/** + * @brief Get the WiFi Mode. + * @return The WiFi Mode. + */ +std::string WiFi::getMode() { + wifi_mode_t mode; + esp_wifi_get_mode(&mode); + switch(mode) { + case WIFI_MODE_NULL: + return "WIFI_MODE_NULL"; + case WIFI_MODE_STA: + return "WIFI_MODE_STA"; + case WIFI_MODE_AP: + return "WIFI_MODE_AP"; + case WIFI_MODE_APSTA: + return "WIFI_MODE_APSTA"; + default: + return "unknown"; + } +} // getMode + + +/** + * @brief Get the STA IP Info. + * @return The STA IP Info. + */ +tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { + tcpip_adapter_ip_info_t ipInfo; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + return ipInfo; +} // getStaIpInfo + +/** + * @brief Get the current ESP32 IP form STA. + * @return The ESP32 IP. + */ +std::string WiFi::getStaIp(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + + +/** + * @brief Get the current STA netmask. + * @return The Netmask IP. + */ +std::string WiFi::getStaNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + + +/** + * @brief Get the current STA Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getStaGateway(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + +/** + * @brief Get the MAC address of the STA interface. + * @return The MAC address of the STA interface. + */ +std::string WiFi::getStaMac() { + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); +} // getStaMac + + +/** + * @brief Get the STA SSID. + * @return The STA SSID. + */ +std::string WiFi::getStaSSID() { + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + return std::string((char *)conf.ap.ssid); +} // getStaSSID + + +/** + * @brief Initialize WiFi. + */ +/* PRIVATE */ void WiFi::init() { + + // If we have already started the event loop, then change the handler otherwise + // start the event loop. + if (m_eventLoopStarted) { + esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. + } else { + esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + m_eventLoopStarted = true; + } + // Now, one way or another, the event handler is WiFi::eventHandler. + + if (!m_initCalled) { + ::nvs_flash_init(); + ::tcpip_adapter_init(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t errRc = ::esp_wifi_init(&cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + m_initCalled = true; +} // init + + +/** + * @brief Perform a WiFi scan looking for access points. + * + * An access point scan is performed and a vector of WiFi access point records + * is built and returned with one record per found scan instance. The scan is + * performed in a blocking fashion and will not return until the set of scanned + * access points has been built. + * + * @return A vector of WiFiAPRecord instances. + */ +std::vector WiFi::scan() { + ESP_LOGD(LOG_TAG, ">> scan"); + std::vector apRecords; + + init(); + + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + wifi_scan_config_t conf; + memset(&conf, 0, sizeof(conf)); + conf.show_hidden = true; + + esp_err_t rc = ::esp_wifi_scan_start(&conf, true); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); + return apRecords; + } + + uint16_t apCount; // Number of access points available. + rc = ::esp_wifi_scan_get_ap_num(&apCount); + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + + wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + if (list == nullptr) { + ESP_LOGE(LOG_TAG, "Failed to allocate memory"); + return apRecords; + } + + errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + for (auto i=0; i rhs.m_rssi;}); + return apRecords; +} // scan + + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { + startAP(ssid, password, auth, 0, false, 4); +} // startAP + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @param[in] channel from the access point. + * @param[in] is the ssid hidden, ore not. + * @param[in] limiting number of clients. + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { + ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); + + init(); + + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + // Build the apConfig structure. + wifi_config_t apConfig; + ::memset(&apConfig, 0, sizeof(apConfig)); + ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); + apConfig.ap.ssid_len = ssid.size(); + ::memcpy(apConfig.ap.password, password.data(), password.size()); + apConfig.ap.channel = channel; + apConfig.ap.authmode = auth; + apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; + apConfig.ap.max_connection = max_connection; + apConfig.ap.beacon_interval = 100; + + errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + + ESP_LOGD(LOG_TAG, "<< startAP"); +} // startAP + +/** + * @brief Set the event handler to use to process detected events. + * @param[in] wifiEventHandler The class that will be used to process events. + */ +void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { + ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); + this->m_pWifiEventHandler = wifiEventHandler; + ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); +} // setWifiEventHandler + + +/** + * @brief Set the IP info and enable DHCP if ip != 0. If called with ip == 0 then DHCP is enabled. + * If called with bad values it will do nothing. + * + * Do not call this method if we are being an access point ourselves. + * + * For example, prior to calling `connectAP()` we could invoke: + * + * @code{.cpp} + * myWifi.setIPInfo("192.168.1.99", "192.168.1.1", "255.255.255.0"); + * @endcode + * + * @param [in] ip IP address value. + * @param [in] gw Gateway value. + * @param [in] netmask Netmask value. + * @return N/A. + */ +void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask) { + setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); +} // setIPInfo + + + +void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { + uint32_t new_ip; + uint32_t new_gw; + uint32_t new_netmask; + + auto success = (bool)inet_pton(AF_INET, ip, &new_ip); + success = success && inet_pton(AF_INET, gw, &new_gw); + success = success && inet_pton(AF_INET, netmask, &new_netmask); + + if(!success) { + return; + } + + setIPInfo(new_ip, new_gw, new_netmask); +} // setIPInfo + + +/** + * @brief Set the IP Info based on the IP address, gateway and netmask. + * @param [in] ip The IP address of our ESP32. + * @param [in] gw The gateway we should use. + * @param [in] netmask Our TCP/IP netmask value. + */ +void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { + init(); + + this->ip = ip; + this->gw = gw; + this->netmask = netmask; + + if(ip != 0 && gw != 0 && netmask != 0) { + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } else { + ip = 0; + ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); + } +} // setIPInfo + + +/** + * @brief Return a string representation of the WiFi access point record. + * + * @return A string representation of the WiFi access point record. + */ +std::string WiFiAPRecord::toString() { + std::string auth; + switch(getAuthMode()) { + case WIFI_AUTH_OPEN: + auth = "WIFI_AUTH_OPEN"; + break; + case WIFI_AUTH_WEP: + auth = "WIFI_AUTH_WEP"; + break; + case WIFI_AUTH_WPA_PSK: + auth = "WIFI_AUTH_WPA_PSK"; + break; + case WIFI_AUTH_WPA2_PSK: + auth = "WIFI_AUTH_WPA2_PSK"; + break; + case WIFI_AUTH_WPA_WPA2_PSK: + auth = "WIFI_AUTH_WPA_WPA2_PSK"; + break; + default: + auth = ""; + break; + } +// std::stringstream s; +// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; + auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); + sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); + return std::string(std::move(info_str)); +} // toString + +/* +MDNS::MDNS() { + esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} + +MDNS::~MDNS() { + if (m_mdns_server != nullptr) { + mdns_free(m_mdns_server); + } + m_mdns_server = nullptr; +} +*/ + +/** + * @brief Define the service for mDNS. + * + * @param [in] service + * @param [in] proto + * @param [in] port + * @return N/A. + */ +/* +void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { + serviceAdd(service.c_str(), proto.c_str(), port); +} // serviceAdd + + +void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { + serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); +} // serviceInstanceSet + + +void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { + servicePortSet(service.c_str(), proto.c_str(), port); +} // servicePortSet + + +void MDNS::serviceRemove(const std::string& service, const std::string& proto) { + serviceRemove(service.c_str(), proto.c_str()); +} // serviceRemove +*/ + +/** + * @brief Set the mDNS hostname. + * + * @param [in] hostname The host name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setHostname(const std::string& hostname) { + setHostname(hostname.c_str()); +} // setHostname +*/ + +/** + * @brief Set the mDNS instance. + * + * @param [in] instance The instance name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setInstance(const std::string& instance) { + setInstance(instance.c_str()); +} // setInstance +*/ + +/** + * @brief Define the service for mDNS. + * + * @param [in] service + * @param [in] proto + * @param [in] port + * @return N/A. + */ +/* +void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { + esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_add: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceAdd + + +void MDNS::serviceInstanceSet(const char* service, const char* proto, const char* instance) { + esp_err_t errRc = ::mdns_service_instance_set(m_mdns_server, service, proto, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_instance_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceInstanceSet + + +void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) { + esp_err_t errRc = ::mdns_service_port_set(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_port_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // servicePortSet + + +void MDNS::serviceRemove(const char* service, const char* proto) { + esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceRemove + +*/ +/** + * @brief Set the mDNS hostname. + * + * @param [in] hostname The host name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setHostname(const char* hostname) { + esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // setHostname +*/ + +/** + * @brief Set the mDNS instance. + * + * @param [in] instance The instance name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setInstance(const char* instance) { + esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // setInstance +*/ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index c1b6bedc..a4d5d4d5 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -1,158 +1,158 @@ -/* - * WiFi.h - * - * Created on: Feb 25, 2017 - * Author: kolban - */ - -#ifndef MAIN_WIFI_H_ -#define MAIN_WIFI_H_ -#include "sdkconfig.h" - -#include -#include -#include -#include -#include "FreeRTOS.h" -#include "WiFiEventHandler.h" - -/** - * @brief Manage mDNS server. - */ -/* -class MDNS { -public: - MDNS(); - ~MDNS(); - void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); - void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); - void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); - void serviceRemove(const std::string& service, const std::string& proto); - void setHostname(const std::string& hostname); - void setInstance(const std::string& instance); - // If we the above functions with a basic char*, a copy would be created into an std::string, - // making the whole thing require twice as much processing power and speed - void serviceAdd(const char* service, const char* proto, uint16_t port); - void serviceInstanceSet(const char* service, const char* proto, const char* instance); - void servicePortSet(const char* service, const char* proto, uint16_t port); - void serviceRemove(const char* service, const char* proto); - void setHostname(const char* hostname); - void setInstance(const char* instance); -private: - mdns_server_t *m_mdns_server = nullptr; -}; -*/ - -class WiFiAPRecord { -public: - friend class WiFi; - - /** - * @brief Get the auth mode. - * @return The auth mode. - */ - wifi_auth_mode_t getAuthMode() { - return m_authMode; - } - - /** - * @brief Get the RSSI. - * @return the RSSI. - */ - int8_t getRSSI() { - return m_rssi; - } - - /** - * @brief Get the SSID. - * @return the SSID. - */ - std::string getSSID() { - return m_ssid; - } - - std::string toString(); - -private: - uint8_t m_bssid[6]; - int8_t m_rssi; - std::string m_ssid; - wifi_auth_mode_t m_authMode; -}; - -/** - * @brief %WiFi driver. - * - * Encapsulate control of %WiFi functions. - * - * Here is an example fragment that illustrates connecting to an access point. - * @code{.cpp} - * #include - * #include - * - * class TargetWiFiEventHandler: public WiFiEventHandler { - * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { - * ESP_LOGD(tag, "MyWiFiEventHandler(Class): staGotIp"); - * // Do something ... - * return ESP_OK; - * } - * }; - * - * WiFi wifi; - * - * TargetWiFiEventHandler *eventHandler = new TargetWiFiEventHandler(); - * wifi.setWifiEventHandler(eventHandler); - * wifi.connectAP("myssid", "mypassword"); - * @endcode - */ -class WiFi { -private: - static esp_err_t eventHandler(void* ctx, system_event_t* event); - void init(); - uint32_t ip; - uint32_t gw; - uint32_t netmask; - WiFiEventHandler* m_pWifiEventHandler; - uint8_t m_dnsCount=0; - bool m_eventLoopStarted; - bool m_initCalled; - uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. - FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); - -public: - WiFi(); - ~WiFi(); - void addDNSServer(const std::string& ip); - void addDNSServer(const char* ip); - void addDNSServer(ip_addr_t ip); - void setDNSServer(int numdns, const std::string& ip); - void setDNSServer(int numdns, const char* ip); - void setDNSServer(int numdns, ip_addr_t ip); - struct in_addr getHostByName(const std::string& hostName); - struct in_addr getHostByName(const char* hostName); - uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); - void dump(); - bool isConnectedToAP(); - static std::string getApMac(); - static tcpip_adapter_ip_info_t getApIpInfo(); - static std::string getApSSID(); - static std::string getApIp(); - static std::string getApNetmask(); - static std::string getApGateway(); - static std::string getMode(); - static tcpip_adapter_ip_info_t getStaIpInfo(); - static std::string getStaMac(); - static std::string getStaSSID(); - static std::string getStaIp(); - static std::string getStaNetmask(); - static std::string getStaGateway(); - std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); - void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); - void setIPInfo(const char* ip, const char* gw, const char* netmask); - void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); -}; - -#endif /* MAIN_WIFI_H_ */ +/* + * WiFi.h + * + * Created on: Feb 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_WIFI_H_ +#define MAIN_WIFI_H_ +#include "sdkconfig.h" + +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "WiFiEventHandler.h" + +/** + * @brief Manage mDNS server. + */ +/* +class MDNS { +public: + MDNS(); + ~MDNS(); + void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); + void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); + void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); + void serviceRemove(const std::string& service, const std::string& proto); + void setHostname(const std::string& hostname); + void setInstance(const std::string& instance); + // If we the above functions with a basic char*, a copy would be created into an std::string, + // making the whole thing require twice as much processing power and speed + void serviceAdd(const char* service, const char* proto, uint16_t port); + void serviceInstanceSet(const char* service, const char* proto, const char* instance); + void servicePortSet(const char* service, const char* proto, uint16_t port); + void serviceRemove(const char* service, const char* proto); + void setHostname(const char* hostname); + void setInstance(const char* instance); +private: + mdns_server_t *m_mdns_server = nullptr; +}; +*/ + +class WiFiAPRecord { +public: + friend class WiFi; + + /** + * @brief Get the auth mode. + * @return The auth mode. + */ + wifi_auth_mode_t getAuthMode() { + return m_authMode; + } + + /** + * @brief Get the RSSI. + * @return the RSSI. + */ + int8_t getRSSI() { + return m_rssi; + } + + /** + * @brief Get the SSID. + * @return the SSID. + */ + std::string getSSID() { + return m_ssid; + } + + std::string toString(); + +private: + uint8_t m_bssid[6]; + int8_t m_rssi; + std::string m_ssid; + wifi_auth_mode_t m_authMode; +}; + +/** + * @brief %WiFi driver. + * + * Encapsulate control of %WiFi functions. + * + * Here is an example fragment that illustrates connecting to an access point. + * @code{.cpp} + * #include + * #include + * + * class TargetWiFiEventHandler: public WiFiEventHandler { + * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { + * ESP_LOGD(tag, "MyWiFiEventHandler(Class): staGotIp"); + * // Do something ... + * return ESP_OK; + * } + * }; + * + * WiFi wifi; + * + * TargetWiFiEventHandler *eventHandler = new TargetWiFiEventHandler(); + * wifi.setWifiEventHandler(eventHandler); + * wifi.connectAP("myssid", "mypassword"); + * @endcode + */ +class WiFi { +private: + static esp_err_t eventHandler(void* ctx, system_event_t* event); + void init(); + uint32_t ip; + uint32_t gw; + uint32_t netmask; + WiFiEventHandler* m_pWifiEventHandler; + uint8_t m_dnsCount=0; + bool m_eventLoopStarted; + bool m_initCalled; + uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. + FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); + +public: + WiFi(); + ~WiFi(); + void addDNSServer(const std::string& ip); + void addDNSServer(const char* ip); + void addDNSServer(ip_addr_t ip); + void setDNSServer(int numdns, const std::string& ip); + void setDNSServer(int numdns, const char* ip); + void setDNSServer(int numdns, ip_addr_t ip); + struct in_addr getHostByName(const std::string& hostName); + struct in_addr getHostByName(const char* hostName); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); + void dump(); + bool isConnectedToAP(); + static std::string getApMac(); + static tcpip_adapter_ip_info_t getApIpInfo(); + static std::string getApSSID(); + static std::string getApIp(); + static std::string getApNetmask(); + static std::string getApGateway(); + static std::string getMode(); + static tcpip_adapter_ip_info_t getStaIpInfo(); + static std::string getStaMac(); + static std::string getStaSSID(); + static std::string getStaIp(); + static std::string getStaNetmask(); + static std::string getStaGateway(); + std::vector scan(); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); + void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); + void setIPInfo(const char* ip, const char* gw, const char* netmask); + void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); + void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); +}; + +#endif /* MAIN_WIFI_H_ */ From fc0a957708fce0442da178c5509b653f0de3e2c8 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sat, 15 Sep 2018 16:47:09 -0600 Subject: [PATCH 244/310] I2C: fix ignored clockSpeed --- cpp_utils/I2C.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 96618bc2..dfcfeff0 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -109,7 +109,7 @@ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t c conf.scl_io_num = sclPin; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; - conf.master.clk_speed = 100000; + conf.master.clk_speed = clockSpeed; esp_err_t errRc = ::i2c_param_config(m_portNum, &conf); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_param_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -279,7 +279,7 @@ void I2C::write(uint8_t byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(val=0x%.2x, ack=%d)", byte, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); if (errRc != ESP_OK) { From de865db4af61119cbbb0147205373ee582f65324 Mon Sep 17 00:00:00 2001 From: Aodzip Date: Sun, 23 Sep 2018 15:29:21 +0800 Subject: [PATCH 245/310] Fix a bug in U8G2 HAL SPI initialize if we don't set bus_config to all zero, a wrong bus_config.flag may have some random bug --- hardware/displays/U8G2/u8g2_esp32_hal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hardware/displays/U8G2/u8g2_esp32_hal.c b/hardware/displays/U8G2/u8g2_esp32_hal.c index fdab51a3..caaa78d9 100644 --- a/hardware/displays/U8G2/u8g2_esp32_hal.c +++ b/hardware/displays/U8G2/u8g2_esp32_hal.c @@ -47,6 +47,7 @@ uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void } spi_bus_config_t bus_config; + memset(&bus_config, 0, sizeof(spi_bus_config_t)); bus_config.sclk_io_num = u8g2_esp32_hal.clk; // CLK bus_config.mosi_io_num = u8g2_esp32_hal.mosi; // MOSI bus_config.miso_io_num = -1; // MISO From 83ca169ef0be97dbe87628878f0ab1b240a41ebd Mon Sep 17 00:00:00 2001 From: hetlelid Date: Wed, 26 Sep 2018 23:41:27 +0200 Subject: [PATCH 246/310] Update GeneralUtils.cpp Changed ESP_LOGD to ESP_LOGV to lower logging level --- cpp_utils/GeneralUtils.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index e26d84c9..2c0f0846 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -109,11 +109,11 @@ void GeneralUtils::dumpInfo() { size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); esp_chip_info_t chipInfo; esp_chip_info(&chipInfo); - ESP_LOGD(LOG_TAG, "--- dumpInfo ---"); - ESP_LOGD(LOG_TAG, "Free heap: %d", freeHeap); - ESP_LOGD(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); - ESP_LOGD(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); - ESP_LOGD(LOG_TAG, "---"); + ESP_LOGV(LOG_TAG, "--- dumpInfo ---"); + ESP_LOGV(LOG_TAG, "Free heap: %d", freeHeap); + ESP_LOGV(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); + ESP_LOGV(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); + ESP_LOGV(LOG_TAG, "---"); } // dumpInfo @@ -231,7 +231,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { if (index % 16 == 0) { strcpy(hexBuf, hex.str().c_str()); strcpy(asciiBuf, ascii.str().c_str()); - ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); hex.str(""); ascii.str(""); } @@ -243,8 +243,8 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { } strcpy(hexBuf, hex.str().c_str()); strcpy(asciiBuf, ascii.str().c_str()); - ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); - //ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); + //ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); } FreeRTOS::sleep(1000); } @@ -266,7 +266,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { } index++; if (index % 16 == 0) { - ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); hex.str(""); ascii.str(""); } @@ -276,7 +276,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { hex << " "; index++; } - ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); } FreeRTOS::sleep(1000); } @@ -296,8 +296,8 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { char tempBuf[80]; uint32_t lineNumber = 0; - ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); - ESP_LOGD(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); + ESP_LOGV(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + ESP_LOGV(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); strcpy(ascii, ""); strcpy(hex, ""); uint32_t index=0; @@ -312,7 +312,7 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { strcat(ascii, tempBuf); index++; if (index % 16 == 0) { - ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); strcpy(ascii, ""); strcpy(hex, ""); lineNumber++; @@ -323,7 +323,7 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { strcat(hex, " "); index++; } - ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); } } // hexDump From 5ce0c5025c76c6f79d53b98d28420d24b88e345f Mon Sep 17 00:00:00 2001 From: Mert Akengin Date: Thu, 27 Sep 2018 23:45:53 +0300 Subject: [PATCH 247/310] update BLECharacteristic.h to include `getData` call --- cpp_utils/BLECharacteristic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index b3f8d2e9..e627628e 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -65,6 +65,7 @@ class BLECharacteristic { //size_t getLength(); BLEUUID getUUID(); std::string getValue(); + uint8_t* getData(); void indicate(); void notify(); From 7e64a01419f5e0b8deead6e4a8f11e621eb0845e Mon Sep 17 00:00:00 2001 From: Mert Akengin Date: Thu, 27 Sep 2018 23:48:06 +0300 Subject: [PATCH 248/310] update BLECharacteristic.cpp to proxy `getData` function --- cpp_utils/BLECharacteristic.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 931c753d..56a27ddb 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -198,6 +198,14 @@ std::string BLECharacteristic::getValue() { return m_value.getValue(); } // getValue +/** + * @brief Retrieve the current raw data of the characteristic. + * @return A pointer to storage containing the current characteristic data. + */ +uint8_t* BLECharacteristic::getData() { + return m_value.getData(); +} // getData + /** * Handle a GATT server event. From cc85e037cd617d3a050bba2b68307698f12e9eab Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 30 Sep 2018 20:08:00 -0600 Subject: [PATCH 249/310] Added switch case to fix compilation --- cpp_utils/BLEUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index a33ee27a..53f1a8f2 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1058,6 +1058,8 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType 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"; } return "Unknown"; } // gattServerEventTypeToString From bb251b7ec39cb0138633469404da2026fed57c18 Mon Sep 17 00:00:00 2001 From: chegewara Date: Mon, 1 Oct 2018 19:39:32 +0200 Subject: [PATCH 250/310] move m_advertising to BLEDevice --- cpp_utils/BLEDevice.cpp | 24 ++++++++++++++++-------- cpp_utils/BLEDevice.h | 8 ++++---- cpp_utils/BLEServer.cpp | 8 +++++--- cpp_utils/BLEServer.h | 2 +- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 369b1285..4da8965a 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -41,6 +41,7 @@ static const char* LOG_TAG = "BLEDevice"; BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; +BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; bool initialized = false; // Have we been initialized? esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; @@ -50,14 +51,13 @@ uint16_t BLEDevice::m_localMTU = 23; * @brief Create a new instance of a client. * @return A new instance of the client. */ -/* STATIC */ BLEClient* BLEDevice::createClient(uint16_t connID) { +/* STATIC */ BLEClient* BLEDevice::createClient() { ESP_LOGD(LOG_TAG, ">> createClient"); #ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); abort(); #endif // CONFIG_GATTC_ENABLE - m_pClient = new BLEClient(connID); - addClient(connID, m_pClient); + m_pClient = new BLEClient(); ESP_LOGD(LOG_TAG, "<< createClient"); return m_pClient; } // createClient @@ -547,12 +547,20 @@ bool BLEDevice::getInitialized() { return initialized; } -void BLEDevice::addClient(uint16_t connID, BLEClient* client) { - // m_clientList.insert(std::pair(connID, client)); +BLEAdvertising* BLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) + { + m_bleAdvertising = new BLEAdvertising(); + ESP_LOGI(LOG_TAG, "create advertising"); + } + ESP_LOGD(LOG_TAG, "get advertising"); + return m_bleAdvertising; } -void BLEDevice::removeClient(uint16_t connID) { - m_clientList.erase(connID); -} +void BLEDevice::startAdvertising() { + ESP_LOGD(LOG_TAG, ">> startAdvertising"); + getAdvertising()->start(); + ESP_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index e4f4ff5a..a37ac960 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -26,8 +26,11 @@ */ class BLEDevice { public: + static BLEAdvertising* getAdvertising(); + static void startAdvertising(); + static BLEAdvertising *m_bleAdvertising; - static BLEClient* createClient(uint16_t connID = 0); // Create a new BLE client. + static BLEClient* createClient(); // Create a new BLE client. static BLEServer* createServer(); // Cretae a new BLE server. static BLEAddress getAddress(); // Retrieve our own local BD address. static BLEScan* getScan(); // Get the scan object @@ -43,8 +46,6 @@ class BLEDevice { static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); static bool getInitialized(); // Returns the state of the device, is it initialized or not? - static void addClient(uint16_t connID, BLEClient* client); - static void removeClient(uint16_t connID); private: static BLEServer *m_pServer; @@ -53,7 +54,6 @@ class BLEDevice { static esp_ble_sec_act_t m_securityLevel; static BLESecurityCallbacks* m_securityCallbacks; static uint16_t m_localMTU; - static std::map m_clientList; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 6a60f1db..56b1e0c0 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -120,7 +120,8 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @return An advertising object. */ BLEAdvertising* BLEServer::getAdvertising() { - return &m_bleAdvertising; + // return &m_bleAdvertising; + return BLEDevice::getAdvertising(); } uint16_t BLEServer::getConnId() { @@ -257,7 +258,7 @@ void BLEServer::handleGATTServerEvent( if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. m_pServerCallbacks->onDisconnect(this); } - startAdvertising(); //- do this with some delay from the loop() + // startAdvertising(); //- do this with some delay from the loop() break; } // ESP_GATTS_DISCONNECT_EVT @@ -359,7 +360,8 @@ void BLEServer::removeService(BLEService *service) { */ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, ">> startAdvertising"); - m_bleAdvertising.start(); + // m_bleAdvertising.start(); + BLEDevice::startAdvertising(); ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index a58b0e9b..7f40faef 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -73,7 +73,7 @@ class BLEServer { friend class BLEDevice; esp_ble_adv_data_t m_adv_data; uint16_t m_appId; - BLEAdvertising m_bleAdvertising; + // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint32_t m_connectedCount; uint16_t m_gatts_if; From b5ab56a60aea1881ebf5257fd4de09fb4f949c84 Mon Sep 17 00:00:00 2001 From: chegewara Date: Mon, 1 Oct 2018 21:44:32 +0200 Subject: [PATCH 251/310] Revert "Move m_advertising to BLEDevice" --- cpp_utils/BLEClient.cpp | 8 +- cpp_utils/BLEClient.h | 2 +- cpp_utils/BLEDevice.cpp | 1114 +++++++++--------- cpp_utils/BLEDevice.h | 3 - cpp_utils/BLERemoteDescriptor.cpp | 370 +++--- cpp_utils/BLEScan.cpp | 584 +++++----- cpp_utils/BLEScan.h | 158 +-- cpp_utils/BLESecurity.h | 142 +-- cpp_utils/BLEServer.cpp | 8 +- cpp_utils/BLEServer.h | 2 +- cpp_utils/BLEService.cpp | 870 +++++++------- cpp_utils/BLEService.h | 208 ++-- cpp_utils/BLEServiceMap.cpp | 264 ++--- cpp_utils/WiFi.cpp | 1808 ++++++++++++++--------------- cpp_utils/WiFi.h | 316 ++--- 15 files changed, 2916 insertions(+), 2941 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 14198d18..141cf0f5 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -21,7 +21,6 @@ #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif -#include "BLEDevice.h" /* * Design @@ -45,9 +44,9 @@ */ static const char* LOG_TAG = "BLEClient"; -BLEClient::BLEClient(uint16_t connID) { +BLEClient::BLEClient() { m_pClientCallbacks = nullptr; - m_conn_id = connID; + m_conn_id = 0; m_gattc_if = 0; m_haveServices = false; m_isConnected = false; // Initially, we are flagged as not connected. @@ -97,7 +96,7 @@ bool BLEClient::connect(BLEAddress address) { clearServices(); // Delete any services that may exist. - esp_err_t errRc = ::esp_ble_gattc_app_register(m_conn_id); + esp_err_t errRc = ::esp_ble_gattc_app_register(0); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; @@ -139,7 +138,6 @@ void BLEClient::disconnect() { } esp_ble_gattc_app_unregister(getGattcIf()); m_peerAddress = BLEAddress("00:00:00:00:00:00"); - BLEDevice::removeClient(m_conn_id); ESP_LOGD(LOG_TAG, "<< disconnect()"); } // disconnect diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index f010b05f..a60ed102 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -28,7 +28,7 @@ class BLEClientCallbacks; */ class BLEClient { public: - BLEClient(uint16_t connID); + BLEClient(); ~BLEClient(); bool connect(BLEAddress address); // Connect to the remote BLE Server diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 4da8965a..a7db454b 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -1,566 +1,548 @@ -/* - * BLE.cpp - * - * Created on: Mar 16, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include -#include -#include -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 BLE -#include // ESP32 ESP-IDF -#include // ESP32 ESP-IDF -#include // Part of C++ Standard library -#include // Part of C++ Standard library -#include // Part of C++ Standard library - -#include "BLEDevice.h" -#include "BLEClient.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#include "esp32-hal-bt.h" -#endif - -static const char* LOG_TAG = "BLEDevice"; - -/** - * Singletons for the BLEDevice. - */ -BLEServer* BLEDevice::m_pServer = nullptr; -BLEScan* BLEDevice::m_pScan = nullptr; -BLEClient* BLEDevice::m_pClient = nullptr; -BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; -bool initialized = false; // Have we been initialized? -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; -BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; -uint16_t BLEDevice::m_localMTU = 23; - -/** - * @brief Create a new instance of a client. - * @return A new instance of the client. - */ -/* STATIC */ BLEClient* BLEDevice::createClient() { - ESP_LOGD(LOG_TAG, ">> createClient"); -#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig - ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); - abort(); -#endif // CONFIG_GATTC_ENABLE - m_pClient = new BLEClient(); - ESP_LOGD(LOG_TAG, "<< createClient"); - return m_pClient; -} // createClient - - -/** - * @brief Create a new instance of a server. - * @return A new instance of the server. - */ -/* STATIC */ BLEServer* BLEDevice::createServer() { - ESP_LOGD(LOG_TAG, ">> createServer"); -#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig - ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); - abort(); -#endif // CONFIG_GATTS_ENABLE - m_pServer = new BLEServer(); - m_pServer->createApp(0); - ESP_LOGD(LOG_TAG, "<< createServer"); - return m_pServer; -} // createServer - - -/** - * @brief Handle GATT server events. - * - * @param [in] event The event that has been newly received. - * @param [in] gatts_if The connection to the GATT interface. - * @param [in] param Parameters for the event. - */ -/* STATIC */ void BLEDevice::gattServerEventHandler( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param -) { - ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", - gatts_if, - BLEUtils::gattServerEventTypeToString(event).c_str()); - - BLEUtils::dumpGattServerEvent(event, gatts_if, param); - - switch(event) { - case ESP_GATTS_CONNECT_EVT: { - BLEDevice::m_localMTU = 23; -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - } // ESP_GATTS_CONNECT_EVT - - case ESP_GATTS_MTU_EVT: { - BLEDevice::m_localMTU = param->mtu.mtu; - ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); - break; - } - default: { - break; - } - } // switch - - - if (BLEDevice::m_pServer != nullptr) { - BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); - } -} // gattServerEventHandler - - -/** - * @brief Handle GATT client events. - * - * Handler for the GATT client events. - * - * @param [in] event - * @param [in] gattc_if - * @param [in] param - */ -/* STATIC */ void BLEDevice::gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* param) { - - ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", - gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); - BLEUtils::dumpGattClientEvent(event, gattc_if, param); - - switch(event) { - case ESP_GATTC_CONNECT_EVT: { - if(BLEDevice::getMTU() != 23){ - esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - } // ESP_GATTC_CONNECT_EVT - - default: { - break; - } - } // switch - - - // If we have a client registered, call it. - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); - } - -} // gattClientEventHandler - - -/** - * @brief Handle GAP events. - */ -/* STATIC */ void BLEDevice::gapEventHandler( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param) { - - BLEUtils::dumpGapEvent(event, param); - - switch(event) { - - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); - break; - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); - break; - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); - break; - case ESP_GAP_BLE_NC_REQ_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); - // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - /* - * TODO should we add white/black list comparison? - */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); - } - else{ - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - /* - * - */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - ///show the passkey number to the user to input it in the peer deivce. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); - BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key type info share with peer device to the user. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); -#endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - } -#endif // CONFIG_BLE_SMP_ENABLE - break; - default: { - break; - } - } // switch - - if (BLEDevice::m_pServer != nullptr) { - BLEDevice::m_pServer->handleGAPEvent(event, param); - } - - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->handleGAPEvent(event, param); - } - - if (BLEDevice::m_pScan != nullptr) { - BLEDevice::getScan()->handleGAPEvent(event, param); - } - - /* - * Security events: - */ - - -} // gapEventHandler - - -/** - * @brief Get the BLE device address. - * @return The BLE device address. - */ -/* STATIC*/ BLEAddress BLEDevice::getAddress() { - const uint8_t* bdAddr = esp_bt_dev_get_address(); - esp_bd_addr_t addr; - memcpy(addr, bdAddr, sizeof(addr)); - return BLEAddress(addr); -} // getAddress - - -/** - * @brief Retrieve the Scan object that we use for scanning. - * @return The scanning object reference. This is a singleton object. The caller should not - * try and release/delete it. - */ -/* STATIC */ BLEScan* BLEDevice::getScan() { - //ESP_LOGD(LOG_TAG, ">> getScan"); - if (m_pScan == nullptr) { - m_pScan = new BLEScan(); - //ESP_LOGD(LOG_TAG, " - creating a new scan object"); - } - //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); - return m_pScan; -} // getScan - - -/** - * @brief Get the value of a characteristic of a service on a remote device. - * @param [in] bdAddress - * @param [in] serviceUUID - * @param [in] characteristicUUID - */ -/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { - ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); - pClient->connect(bdAddress); - std::string ret = pClient->getValue(serviceUUID, characteristicUUID); - pClient->disconnect(); - ESP_LOGD(LOG_TAG, "<< getValue"); - return ret; -} // getValue - - -/** - * @brief Initialize the %BLE environment. - * @param deviceName The device name of the device. - */ -/* STATIC */ void BLEDevice::init(std::string deviceName) { - if(!initialized){ - initialized = true; // Set the initialization flag to ensure we are only initialized once. - - esp_err_t errRc = ESP_OK; -#ifdef ARDUINO_ARCH_ESP32 - if (!btStart()) { - errRc = ESP_FAIL; - return; - } -#else - errRc = ::nvs_flash_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - errRc = esp_bt_controller_init(&bt_cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - -#ifndef CLASSIC_BT_ENABLED - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue - errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#else - errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif -#endif - - esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); - if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ - errRc = esp_bluedroid_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ - errRc = esp_bluedroid_enable(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - -#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig - errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif // CONFIG_GATTC_ENABLE - -#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig - errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -#endif // CONFIG_GATTS_ENABLE - - errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; - -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; - errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - }; -#endif // CONFIG_BLE_SMP_ENABLE - } - vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. -} // init - - -/** - * @brief Set the transmission power. - * The power level can be one of: - * * ESP_PWR_LVL_N14 - * * ESP_PWR_LVL_N11 - * * ESP_PWR_LVL_N8 - * * ESP_PWR_LVL_N5 - * * ESP_PWR_LVL_N2 - * * ESP_PWR_LVL_P1 - * * ESP_PWR_LVL_P4 - * * ESP_PWR_LVL_P7 - * @param [in] powerLevel. - */ -/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { - ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); - esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - }; - ESP_LOGD(LOG_TAG, "<< setPower"); -} // setPower - - -/** - * @brief Set the value of a characteristic of a service on a remote device. - * @param [in] bdAddress - * @param [in] serviceUUID - * @param [in] characteristicUUID - */ -/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { - ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); - pClient->connect(bdAddress); - pClient->setValue(serviceUUID, characteristicUUID, value); - pClient->disconnect(); -} // setValue - - -/** - * @brief Return a string representation of the nature of this device. - * @return A string representation of the nature of this device. - */ -/* STATIC */ std::string BLEDevice::toString() { - std::ostringstream oss; - oss << "BD Address: " << getAddress().toString(); - return oss.str(); -} // toString - - -/** - * @brief Add an entry to the BLE white list. - * @param [in] address The address to add to the white list. - */ -void BLEDevice::whiteListAdd(BLEAddress address) { - ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); - esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - ESP_LOGD(LOG_TAG, "<< whiteListAdd"); -} // whiteListAdd - - -/** - * @brief Remove an entry from the BLE white list. - * @param [in] address The address to remove from the white list. - */ -void BLEDevice::whiteListRemove(BLEAddress address) { - ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); - esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - ESP_LOGD(LOG_TAG, "<< whiteListRemove"); -} // whiteListRemove - -/* - * @brief Set encryption level that will be negotiated with peer device durng connection - * @param [in] level Requested encryption level - */ -void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { - BLEDevice::m_securityLevel = level; -} - -/* - * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events - * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback - */ -void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { - BLEDevice::m_securityCallbacks = callbacks; -} - -/* - * @brief Setup local mtu that will be used to negotiate mtu during request from client peer - * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 - */ -esp_err_t BLEDevice::setMTU(uint16_t mtu) { - ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); - esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); - if(err == ESP_OK){ - m_localMTU = mtu; - } else { - ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); - } - ESP_LOGD(LOG_TAG, "<< setLocalMTU"); - return err; -} - -/* - * @brief Get local MTU value set during mtu request or default value - */ -uint16_t BLEDevice::getMTU() { - return m_localMTU; -} - -bool BLEDevice::getInitialized() { - return initialized; -} - -BLEAdvertising* BLEDevice::getAdvertising() { - if(m_bleAdvertising == nullptr) - { - m_bleAdvertising = new BLEAdvertising(); - ESP_LOGI(LOG_TAG, "create advertising"); - } - ESP_LOGD(LOG_TAG, "get advertising"); - return m_bleAdvertising; -} - -void BLEDevice::startAdvertising() { - ESP_LOGD(LOG_TAG, ">> startAdvertising"); - getAdvertising()->start(); - ESP_LOGD(LOG_TAG, "<< startAdvertising"); -} // startAdvertising - -#endif // CONFIG_BT_ENABLED +/* + * BLE.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 ESP-IDF +#include // ESP32 ESP-IDF +#include // Part of C++ Standard library +#include // Part of C++ Standard library +#include // Part of C++ Standard library + +#include "BLEDevice.h" +#include "BLEClient.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#include "esp32-hal-bt.h" +#endif + +static const char* LOG_TAG = "BLEDevice"; + +/** + * Singletons for the BLEDevice. + */ +BLEServer* BLEDevice::m_pServer = nullptr; +BLEScan* BLEDevice::m_pScan = nullptr; +BLEClient* BLEDevice::m_pClient = nullptr; +bool initialized = false; // Have we been initialized? +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; +BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; +uint16_t BLEDevice::m_localMTU = 23; + +/** + * @brief Create a new instance of a client. + * @return A new instance of the client. + */ +/* STATIC */ BLEClient* BLEDevice::createClient() { + ESP_LOGD(LOG_TAG, ">> createClient"); +#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTC_ENABLE + m_pClient = new BLEClient(); + ESP_LOGD(LOG_TAG, "<< createClient"); + return m_pClient; +} // createClient + + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +/* STATIC */ BLEServer* BLEDevice::createServer() { + ESP_LOGD(LOG_TAG, ">> createServer"); +#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE + m_pServer = new BLEServer(); + m_pServer->createApp(0); + ESP_LOGD(LOG_TAG, "<< createServer"); + return m_pServer; +} // createServer + + +/** + * @brief Handle GATT server events. + * + * @param [in] event The event that has been newly received. + * @param [in] gatts_if The connection to the GATT interface. + * @param [in] param Parameters for the event. + */ +/* STATIC */ void BLEDevice::gattServerEventHandler( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param +) { + ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", + gatts_if, + BLEUtils::gattServerEventTypeToString(event).c_str()); + + BLEUtils::dumpGattServerEvent(event, gatts_if, param); + + switch(event) { + case ESP_GATTS_CONNECT_EVT: { + BLEDevice::m_localMTU = 23; +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTS_CONNECT_EVT + + case ESP_GATTS_MTU_EVT: { + BLEDevice::m_localMTU = param->mtu.mtu; + ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); + break; + } + default: { + break; + } + } // switch + + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); + } +} // gattServerEventHandler + + +/** + * @brief Handle GATT client events. + * + * Handler for the GATT client events. + * + * @param [in] event + * @param [in] gattc_if + * @param [in] param + */ +/* STATIC */ void BLEDevice::gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + BLEUtils::dumpGattClientEvent(event, gattc_if, param); + + switch(event) { + case ESP_GATTC_CONNECT_EVT: { + if(BLEDevice::getMTU() != 23){ + esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTC_CONNECT_EVT + + default: { + break; + } + } // switch + + + // If we have a client registered, call it. + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); + } + +} // gattClientEventHandler + + +/** + * @brief Handle GAP events. + */ +/* STATIC */ void BLEDevice::gapEventHandler( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t *param) { + + BLEUtils::dumpGapEvent(event, param); + + switch(event) { + + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); + } + else{ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer deivce. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + default: { + break; + } + } // switch + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGAPEvent(event, param); + } + + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->handleGAPEvent(event, param); + } + + if (BLEDevice::m_pScan != nullptr) { + BLEDevice::getScan()->handleGAPEvent(event, param); + } + + /* + * Security events: + */ + + +} // gapEventHandler + + +/** + * @brief Get the BLE device address. + * @return The BLE device address. + */ +/* STATIC*/ BLEAddress BLEDevice::getAddress() { + const uint8_t* bdAddr = esp_bt_dev_get_address(); + esp_bd_addr_t addr; + memcpy(addr, bdAddr, sizeof(addr)); + return BLEAddress(addr); +} // getAddress + + +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. + */ +/* STATIC */ BLEScan* BLEDevice::getScan() { + //ESP_LOGD(LOG_TAG, ">> getScan"); + if (m_pScan == nullptr) { + m_pScan = new BLEScan(); + //ESP_LOGD(LOG_TAG, " - creating a new scan object"); + } + //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); + return m_pScan; +} // getScan + + +/** + * @brief Get the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + std::string ret = pClient->getValue(serviceUUID, characteristicUUID); + pClient->disconnect(); + ESP_LOGD(LOG_TAG, "<< getValue"); + return ret; +} // getValue + + +/** + * @brief Initialize the %BLE environment. + * @param deviceName The device name of the device. + */ +/* STATIC */ void BLEDevice::init(std::string deviceName) { + if(!initialized){ + initialized = true; // Set the initialization flag to ensure we are only initialized once. + + esp_err_t errRc = ESP_OK; +#ifdef ARDUINO_ARCH_ESP32 + if (!btStart()) { + errRc = ESP_FAIL; + return; + } +#else + errRc = ::nvs_flash_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + errRc = esp_bt_controller_init(&bt_cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifndef CLASSIC_BT_ENABLED + // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#else + errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif +#endif + + esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); + if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ + errRc = esp_bluedroid_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ + errRc = esp_bluedroid_enable(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig + errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTC_ENABLE + +#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig + errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTS_ENABLE + + errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; + +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; + errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; +#endif // CONFIG_BLE_SMP_ENABLE + } + vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. +} // init + + +/** + * @brief Set the transmission power. + * The power level can be one of: + * * ESP_PWR_LVL_N14 + * * ESP_PWR_LVL_N11 + * * ESP_PWR_LVL_N8 + * * ESP_PWR_LVL_N5 + * * ESP_PWR_LVL_N2 + * * ESP_PWR_LVL_P1 + * * ESP_PWR_LVL_P4 + * * ESP_PWR_LVL_P7 + * @param [in] powerLevel. + */ +/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { + ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); + esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + }; + ESP_LOGD(LOG_TAG, "<< setPower"); +} // setPower + + +/** + * @brief Set the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient *pClient = createClient(); + pClient->connect(bdAddress); + pClient->setValue(serviceUUID, characteristicUUID, value); + pClient->disconnect(); +} // setValue + + +/** + * @brief Return a string representation of the nature of this device. + * @return A string representation of the nature of this device. + */ +/* STATIC */ std::string BLEDevice::toString() { + std::ostringstream oss; + oss << "BD Address: " << getAddress().toString(); + return oss.str(); +} // toString + + +/** + * @brief Add an entry to the BLE white list. + * @param [in] address The address to add to the white list. + */ +void BLEDevice::whiteListAdd(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListAdd"); +} // whiteListAdd + + +/** + * @brief Remove an entry from the BLE white list. + * @param [in] address The address to remove from the white list. + */ +void BLEDevice::whiteListRemove(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListRemove"); +} // whiteListRemove + +/* + * @brief Set encryption level that will be negotiated with peer device durng connection + * @param [in] level Requested encryption level + */ +void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { + BLEDevice::m_securityLevel = level; +} + +/* + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback + */ +void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + BLEDevice::m_securityCallbacks = callbacks; +} + +/* + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer + * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 + */ +esp_err_t BLEDevice::setMTU(uint16_t mtu) { + ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); + if(err == ESP_OK){ + m_localMTU = mtu; + } else { + ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); + } + ESP_LOGD(LOG_TAG, "<< setLocalMTU"); + return err; +} + +/* + * @brief Get local MTU value set during mtu request or default value + */ +uint16_t BLEDevice::getMTU() { + return m_localMTU; +} + +bool BLEDevice::getInitialized() { + return initialized; +} +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index a37ac960..7a1b833d 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -26,9 +26,6 @@ */ class BLEDevice { public: - static BLEAdvertising* getAdvertising(); - static void startAdvertising(); - static BLEAdvertising *m_bleAdvertising; static BLEClient* createClient(); // Create a new BLE client. static BLEServer* createServer(); // Cretae a new BLE server. diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 36492360..2cdc7db1 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -1,185 +1,185 @@ -/* - * BLERemoteDescriptor.cpp - * - * Created on: Jul 8, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include "BLERemoteDescriptor.h" -#include "GeneralUtils.h" -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -static const char* LOG_TAG = "BLERemoteDescriptor"; - - -BLERemoteDescriptor::BLERemoteDescriptor( - uint16_t handle, - BLEUUID uuid, - BLERemoteCharacteristic* pRemoteCharacteristic) { - - m_handle = handle; - m_uuid = uuid; - m_pRemoteCharacteristic = pRemoteCharacteristic; -} - - -/** - * @brief Retrieve the handle associated with this remote descriptor. - * @return The handle associated with this remote descriptor. - */ -uint16_t BLERemoteDescriptor::getHandle() { - return m_handle; -} // getHandle - - -/** - * @brief Get the characteristic that owns this descriptor. - * @return The characteristic that owns this descriptor. - */ -BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { - return m_pRemoteCharacteristic; -} // getRemoteCharacteristic - - -/** - * @brief Retrieve the UUID associated this remote descriptor. - * @return The UUID associated this remote descriptor. - */ -BLEUUID BLERemoteDescriptor::getUUID() { - return m_uuid; -} // getUUID - - -std::string BLERemoteDescriptor::readValue(void) { - ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); - - // Check to see that we are connected. - if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { - ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); - } - - m_semaphoreReadDescrEvt.take("readValue"); - - // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. - esp_err_t errRc = ::esp_ble_gattc_read_char_descr( - m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), - m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server - getHandle(), // The handle of this characteristic - ESP_GATT_AUTH_REQ_NONE); // Security - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return ""; - } - - // Block waiting for the event that indicates that the read has completed. When it has, the std::string found - // in m_value will contain our data. - m_semaphoreReadDescrEvt.wait("readValue"); - - ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); - return m_value; -} // readValue - - -uint8_t BLERemoteDescriptor::readUInt8(void) { - std::string value = readValue(); - if (value.length() >= 1) { - return (uint8_t)value[0]; - } - return 0; -} // readUInt8 - - -uint16_t BLERemoteDescriptor::readUInt16(void) { - std::string value = readValue(); - if (value.length() >= 2) { - return *(uint16_t*)(value.data()); - } - return 0; -} // readUInt16 - - -uint32_t BLERemoteDescriptor::readUInt32(void) { - std::string value = readValue(); - if (value.length() >= 4) { - return *(uint32_t*)(value.data()); - } - return 0; -} // readUInt32 - - -/** - * @brief Return a string representation of this BLE Remote Descriptor. - * @retun A string representation of this BLE Remote Descriptor. - */ -std::string BLERemoteDescriptor::toString(void) { - std::stringstream ss; - ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); - return ss.str(); -} // toString - - -/** - * @brief Write data to the BLE Remote Descriptor. - * @param [in] data The data to send to the remote descriptor. - * @param [in] length The length of the data to send. - * @param [in] response True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - uint8_t* data, - size_t length, - bool response) { - ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); - // Check to see that we are connected. - if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { - ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); - } - - esp_err_t errRc = ::esp_ble_gattc_write_char_descr( - m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), - m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), - getHandle(), - length, // Data length - data, // Data - response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE - ); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); - } - ESP_LOGD(LOG_TAG, "<< writeValue"); -} // writeValue - - -/** - * @brief Write data represented as a string to the BLE Remote Descriptor. - * @param [in] newValue The data to send to the remote descriptor. - * @param [in] response True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - std::string newValue, - bool response) { - writeValue(newValue.data(), newValue.length()); -} // writeValue - - -/** - * @brief Write a byte value to the Descriptor. - * @param [in] The single byte to write. - * @param [in] True if we expect a response. - */ -void BLERemoteDescriptor::writeValue( - uint8_t newValue, - bool response) { - writeValue(&newValue, 1, response); -} // writeValue - - -#endif /* CONFIG_BT_ENABLED */ +/* + * BLERemoteDescriptor.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLERemoteDescriptor.h" +#include "GeneralUtils.h" +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +static const char* LOG_TAG = "BLERemoteDescriptor"; + + +BLERemoteDescriptor::BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic) { + + m_handle = handle; + m_uuid = uuid; + m_pRemoteCharacteristic = pRemoteCharacteristic; +} + + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t BLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +BLEUUID BLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +std::string BLERemoteDescriptor::readValue(void) { + ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); + + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + m_semaphoreReadDescrEvt.take("readValue"); + + // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + esp_err_t errRc = ::esp_ble_gattc_read_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return ""; + } + + // Block waiting for the event that indicates that the read has completed. When it has, the std::string found + // in m_value will contain our data. + m_semaphoreReadDescrEvt.wait("readValue"); + + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return m_value; +} // readValue + + +uint8_t BLERemoteDescriptor::readUInt8(void) { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t)value[0]; + } + return 0; +} // readUInt8 + + +uint16_t BLERemoteDescriptor::readUInt16(void) { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*)(value.data()); + } + return 0; +} // readUInt16 + + +uint32_t BLERemoteDescriptor::readUInt32(void) { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*)(value.data()); + } + return 0; +} // readUInt32 + + +/** + * @brief Return a string representation of this BLE Remote Descriptor. + * @retun A string representation of this BLE Remote Descriptor. + */ +std::string BLERemoteDescriptor::toString(void) { + std::stringstream ss; + ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); + return ss.str(); +} // toString + + +/** + * @brief Write data to the BLE Remote Descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t* data, + size_t length, + bool response) { + ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + esp_err_t errRc = ::esp_ble_gattc_write_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), + getHandle(), + length, // Data length + data, // Data + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); + } + ESP_LOGD(LOG_TAG, "<< writeValue"); +} // writeValue + + +/** + * @brief Write data represented as a string to the BLE Remote Descriptor. + * @param [in] newValue The data to send to the remote descriptor. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + std::string newValue, + bool response) { + writeValue(newValue.data(), newValue.length()); +} // writeValue + + +/** + * @brief Write a byte value to the Descriptor. + * @param [in] The single byte to write. + * @param [in] True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t newValue, + bool response) { + writeValue(&newValue, 1, response); +} // writeValue + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 87046670..3046b7c8 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -1,292 +1,292 @@ -/* - * BLEScan.cpp - * - * Created on: Jul 1, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - - -#include -#include - -#include - -#include "BLEAdvertisedDevice.h" -#include "BLEScan.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -static const char* LOG_TAG = "BLEScan"; - - -/** - * Constructor - */ -BLEScan::BLEScan() { - m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. - m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; - m_pAdvertisedDeviceCallbacks = nullptr; - m_stopped = true; - m_wantDuplicates = false; - setInterval(100); - setWindow(100); -} // BLEScan - - -/** - * @brief Handle GAP events related to scans. - * @param [in] event The event type for this event. - * @param [in] param Parameter data for this event. - */ -void BLEScan::handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { - - switch(event) { - - // ESP_GAP_BLE_SCAN_RESULT_EVT - // --------------------------- - // scan_rst: - // esp_gap_search_evt_t search_evt - // esp_bd_addr_t bda - // esp_bt_dev_type_t dev_type - // esp_ble_addr_type_t ble_addr_type - // esp_ble_evt_type_t ble_evt_type - // int rssi - // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] - // int flag - // int num_resps - // uint8_t adv_data_len - // uint8_t scan_rsp_len - case ESP_GAP_BLE_SCAN_RESULT_EVT: { - - switch(param->scan_rst.search_evt) { - // - // ESP_GAP_SEARCH_INQ_CMPL_EVT - // - // Event that indicates that the duration allowed for the search has completed or that we have been - // asked to stop. - case ESP_GAP_SEARCH_INQ_CMPL_EVT: { - m_stopped = true; - if (m_scanCompleteCB != nullptr) { - m_scanCompleteCB(m_scanResults); - } - m_semaphoreScanEnd.give(); - break; - } // ESP_GAP_SEARCH_INQ_CMPL_EVT - - // - // ESP_GAP_SEARCH_INQ_RES_EVT - // - // Result that has arrived back from a Scan inquiry. - case ESP_GAP_SEARCH_INQ_RES_EVT: { - if (m_stopped) { // If we are not scanning, nothing to do with the extra results. - break; - } - -// Examine our list of previously scanned addresses and, if we found this one already, -// ignore it. - BLEAddress advertisedAddress(param->scan_rst.bda); - bool found = false; - - for (int i=0; iscan_rst.rssi); - advertisedDevice.setAdFlag(param->scan_rst.flag); - advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); - advertisedDevice.setScan(this); - - if (m_pAdvertisedDeviceCallbacks) { - m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); - } - - if (!found) { // If we have previously seen this device, don't record it again. - m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); - } - - break; - } // ESP_GAP_SEARCH_INQ_RES_EVT - - default: { - break; - } - } // switch - search_evt - - - break; - } // ESP_GAP_BLE_SCAN_RESULT_EVT - - default: { - break; - } // default - } // End switch -} // gapEventHandler - - -/** - * @brief Should we perform an active or passive scan? - * The default is a passive scan. An active scan means that we will wish a scan response. - * @param [in] active If true, we perform an active scan otherwise a passive scan. - * @return N/A. - */ -void BLEScan::setActiveScan(bool active) { - if (active) { - m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; - } else { - m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; - } -} // setActiveScan - - -/** - * @brief Set the call backs to be invoked. - * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. - * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. - */ -void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { - m_wantDuplicates = wantDuplicates; - m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; -} // setAdvertisedDeviceCallbacks - - -/** - * @brief Set the interval to scan. - * @param [in] The interval in msecs. - */ -void BLEScan::setInterval(uint16_t intervalMSecs) { - m_scan_params.scan_interval = intervalMSecs / 0.625; -} // setInterval - - -/** - * @brief Set the window to actively scan. - * @param [in] windowMSecs How long to actively scan. - */ -void BLEScan::setWindow(uint16_t windowMSecs) { - m_scan_params.scan_window = windowMSecs / 0.625; -} // setWindow - - -/** - * @brief Start scanning. - * @param [in] duration The duration in seconds for which to scan. - * @param [in] scanCompleteCB A function to be called when scanning has completed. - * @return True if scan started or false if there was an error. - */ -bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { - ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); - - m_semaphoreScanEnd.take(std::string("start")); - m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. - - m_scanResults.m_vectorAdvertisedDevices.clear(); - - esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - m_semaphoreScanEnd.give(); - return false; - } - - errRc = ::esp_ble_gap_start_scanning(duration); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - m_semaphoreScanEnd.give(); - return false; - } - - m_stopped = false; - - ESP_LOGD(LOG_TAG, "<< start()"); - return true; -} // start - - -/** - * @brief Start scanning and block until scanning has been completed. - * @param [in] duration The duration in seconds for which to scan. - * @return The BLEScanResults. - */ -BLEScanResults BLEScan::start(uint32_t duration) { - if(start(duration, nullptr)) { - m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. - } - return m_scanResults; -} // start - - -/** - * @brief Stop an in progress scan. - * @return N/A. - */ -void BLEScan::stop() { - ESP_LOGD(LOG_TAG, ">> stop()"); - - esp_err_t errRc = ::esp_ble_gap_stop_scanning(); - - m_stopped = true; - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreScanEnd.give(); - - ESP_LOGD(LOG_TAG, "<< stop()"); -} // stop - - -/** - * @brief Dump the scan results to the log. - */ -void BLEScanResults::dump() { - ESP_LOGD(LOG_TAG, ">> Dump scan results:"); - for (int i=0; i +#include + +#include + +#include "BLEAdvertisedDevice.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +static const char* LOG_TAG = "BLEScan"; + + +/** + * Constructor + */ +BLEScan::BLEScan() { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. + m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; + m_wantDuplicates = false; + setInterval(100); + setWindow(100); +} // BLEScan + + +/** + * @brief Handle GAP events related to scans. + * @param [in] event The event type for this event. + * @param [in] param Parameter data for this event. + */ +void BLEScan::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + switch(event) { + + // ESP_GAP_BLE_SCAN_RESULT_EVT + // --------------------------- + // scan_rst: + // esp_gap_search_evt_t search_evt + // esp_bd_addr_t bda + // esp_bt_dev_type_t dev_type + // esp_ble_addr_type_t ble_addr_type + // esp_ble_evt_type_t ble_evt_type + // int rssi + // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] + // int flag + // int num_resps + // uint8_t adv_data_len + // uint8_t scan_rsp_len + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + + switch(param->scan_rst.search_evt) { + // + // ESP_GAP_SEARCH_INQ_CMPL_EVT + // + // Event that indicates that the duration allowed for the search has completed or that we have been + // asked to stop. + case ESP_GAP_SEARCH_INQ_CMPL_EVT: { + m_stopped = true; + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } + m_semaphoreScanEnd.give(); + break; + } // ESP_GAP_SEARCH_INQ_CMPL_EVT + + // + // ESP_GAP_SEARCH_INQ_RES_EVT + // + // Result that has arrived back from a Scan inquiry. + case ESP_GAP_SEARCH_INQ_RES_EVT: { + if (m_stopped) { // If we are not scanning, nothing to do with the extra results. + break; + } + +// Examine our list of previously scanned addresses and, if we found this one already, +// ignore it. + BLEAddress advertisedAddress(param->scan_rst.bda); + bool found = false; + + for (int i=0; iscan_rst.rssi); + advertisedDevice.setAdFlag(param->scan_rst.flag); + advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); + advertisedDevice.setScan(this); + + if (m_pAdvertisedDeviceCallbacks) { + m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + } + + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + } + + break; + } // ESP_GAP_SEARCH_INQ_RES_EVT + + default: { + break; + } + } // switch - search_evt + + + break; + } // ESP_GAP_BLE_SCAN_RESULT_EVT + + default: { + break; + } // default + } // End switch +} // gapEventHandler + + +/** + * @brief Should we perform an active or passive scan? + * The default is a passive scan. An active scan means that we will wish a scan response. + * @param [in] active If true, we perform an active scan otherwise a passive scan. + * @return N/A. + */ +void BLEScan::setActiveScan(bool active) { + if (active) { + m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; + } else { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; + } +} // setActiveScan + + +/** + * @brief Set the call backs to be invoked. + * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + */ +void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { + m_wantDuplicates = wantDuplicates; + m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; +} // setAdvertisedDeviceCallbacks + + +/** + * @brief Set the interval to scan. + * @param [in] The interval in msecs. + */ +void BLEScan::setInterval(uint16_t intervalMSecs) { + m_scan_params.scan_interval = intervalMSecs / 0.625; +} // setInterval + + +/** + * @brief Set the window to actively scan. + * @param [in] windowMSecs How long to actively scan. + */ +void BLEScan::setWindow(uint16_t windowMSecs) { + m_scan_params.scan_window = windowMSecs / 0.625; +} // setWindow + + +/** + * @brief Start scanning. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @return True if scan started or false if there was an error. + */ +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { + ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + + m_semaphoreScanEnd.take(std::string("start")); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. + + m_scanResults.m_vectorAdvertisedDevices.clear(); + + esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + errRc = ::esp_ble_gap_start_scanning(duration); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + m_stopped = false; + + ESP_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +BLEScanResults BLEScan::start(uint32_t duration) { + if(start(duration, nullptr)) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } + return m_scanResults; +} // start + + +/** + * @brief Stop an in progress scan. + * @return N/A. + */ +void BLEScan::stop() { + ESP_LOGD(LOG_TAG, ">> stop()"); + + esp_err_t errRc = ::esp_ble_gap_stop_scanning(); + + m_stopped = true; + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreScanEnd.give(); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // stop + + +/** + * @brief Dump the scan results to the log. + */ +void BLEScanResults::dump() { + ESP_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; i - -#include -#include "BLEAdvertisedDevice.h" -#include "BLEClient.h" -#include "FreeRTOS.h" - -class BLEAdvertisedDevice; -class BLEAdvertisedDeviceCallbacks; -class BLEClient; -class BLEScan; - - -/** - * @brief The result of having performed a scan. - * When a scan completes, we have a set of found devices. Each device is described - * by a BLEAdvertisedDevice object. The number of items in the set is given by - * getCount(). We can retrieve a device by calling getDevice() passing in the - * index (starting at 0) of the desired device. - */ -class BLEScanResults { -public: - void dump(); - int getCount(); - BLEAdvertisedDevice getDevice(uint32_t i); - -private: - friend BLEScan; - std::vector m_vectorAdvertisedDevices; -}; - -/** - * @brief Perform and manage %BLE scans. - * - * Scanning is associated with a %BLE client that is attempting to locate BLE servers. - */ -class BLEScan { -public: - void setActiveScan(bool active); - void setAdvertisedDeviceCallbacks( - BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, - bool wantDuplicates = false); - void setInterval(uint16_t intervalMSecs); - void setWindow(uint16_t windowMSecs); - bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); - BLEScanResults start(uint32_t duration); - void stop(); - -private: - BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. - friend class BLEDevice; - void handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param); - void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); - - - esp_ble_scan_params_t m_scan_params; - BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; - bool m_stopped; - FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); - BLEScanResults m_scanResults; - bool m_wantDuplicates; - void (*m_scanCompleteCB)(BLEScanResults scanResults); -}; // BLEScan - -#endif /* CONFIG_BT_ENABLED */ -#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ +/* + * BLEScan.h + * + * Created on: Jul 1, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ +#define COMPONENTS_CPP_UTILS_BLESCAN_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +#include +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "FreeRTOS.h" + +class BLEAdvertisedDevice; +class BLEAdvertisedDeviceCallbacks; +class BLEClient; +class BLEScan; + + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ +class BLEScanResults { +public: + void dump(); + int getCount(); + BLEAdvertisedDevice getDevice(uint32_t i); + +private: + friend BLEScan; + std::vector m_vectorAdvertisedDevices; +}; + +/** + * @brief Perform and manage %BLE scans. + * + * Scanning is associated with a %BLE client that is attempting to locate BLE servers. + */ +class BLEScan { +public: + void setActiveScan(bool active); + void setAdvertisedDeviceCallbacks( + BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates = false); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); + BLEScanResults start(uint32_t duration); + void stop(); + +private: + BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. + friend class BLEDevice; + void handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); + + + esp_ble_scan_params_t m_scan_params; + BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; + bool m_stopped; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + BLEScanResults m_scanResults; + bool m_wantDuplicates; + void (*m_scanCompleteCB)(BLEScanResults scanResults); +}; // BLEScan + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index e35a398a..2d52b015 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -1,71 +1,71 @@ -/* - * BLESecurity.h - * - * Created on: Dec 17, 2017 - * Author: chegewara - */ - -#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ -#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include - -class BLESecurity { -public: - BLESecurity(); - virtual ~BLESecurity(); - void setAuthenticationMode(esp_ble_auth_req_t auth_req); - void setCapability(esp_ble_io_cap_t iocap); - void setInitEncryptionKey(uint8_t init_key); - void setRespEncryptionKey(uint8_t resp_key); - void setKeySize(uint8_t key_size = 16); - static char* esp_key_type_to_str(esp_ble_key_type_t key_type); - -private: - esp_ble_auth_req_t m_authReq; - esp_ble_io_cap_t m_iocap; - uint8_t m_initKey; - uint8_t m_respKey; - uint8_t m_keySize; -}; // BLESecurity - - -/* - * @brief Callbacks to handle GAP events related to authorization - */ -class BLESecurityCallbacks { -public: - virtual ~BLESecurityCallbacks() {}; - - /** - * @brief Its request from peer device to input authentication pin code displayed on peer device. - * It requires that our device is capable to input 6-digits code by end user - * @return Return 6-digits integer value from input device - */ - virtual uint32_t onPassKeyRequest() = 0; - - /** - * @brief Provide us 6-digits code to perform authentication. - * It requires that our device is capable to display this code to end user - * @param - */ - virtual void onPassKeyNotify(uint32_t pass_key) = 0; - - /** - * @brief Here we can make decision if we want to let negotiate authorization with peer device or not - * return Return true if we accept this peer device request - */ - - virtual bool onSecurityRequest() = 0 ; - /** - * Provide us information when authentication process is completed - */ - virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; - - virtual bool onConfirmPIN(uint32_t pin) = 0; -}; // BLESecurityCallbacks - -#endif // CONFIG_BT_ENABLED -#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ +/* + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +class BLESecurity { +public: + BLESecurity(); + virtual ~BLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + static char* esp_key_type_to_str(esp_ble_key_type_t key_type); + +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; +}; // BLESecurity + + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class BLESecurityCallbacks { +public: + virtual ~BLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + + virtual bool onSecurityRequest() = 0 ; + /** + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 56b1e0c0..6a60f1db 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -120,8 +120,7 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @return An advertising object. */ BLEAdvertising* BLEServer::getAdvertising() { - // return &m_bleAdvertising; - return BLEDevice::getAdvertising(); + return &m_bleAdvertising; } uint16_t BLEServer::getConnId() { @@ -258,7 +257,7 @@ void BLEServer::handleGATTServerEvent( if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. m_pServerCallbacks->onDisconnect(this); } - // startAdvertising(); //- do this with some delay from the loop() + startAdvertising(); //- do this with some delay from the loop() break; } // ESP_GATTS_DISCONNECT_EVT @@ -360,8 +359,7 @@ void BLEServer::removeService(BLEService *service) { */ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, ">> startAdvertising"); - // m_bleAdvertising.start(); - BLEDevice::startAdvertising(); + m_bleAdvertising.start(); ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 7f40faef..a58b0e9b 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -73,7 +73,7 @@ class BLEServer { friend class BLEDevice; esp_ble_adv_data_t m_adv_data; uint16_t m_appId; - // BLEAdvertising m_bleAdvertising; + BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint32_t m_connectedCount; uint16_t m_gatts_if; diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 863eb66f..340ea560 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -1,435 +1,435 @@ -/* - * BLEService.cpp - * - * Created on: Mar 25, 2017 - * Author: kolban - */ - -// A service is identified by a UUID. A service is also the container for one or more characteristics. - -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include - -#include -#include -#include - -#include "BLEServer.h" -#include "BLEService.h" -#include "BLEUtils.h" -#include "GeneralUtils.h" - -#ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" -#endif - -#define NULL_HANDLE (0xffff) - -static const char* LOG_TAG = "BLEService"; // Tag for logging. - -/** - * @brief Construct an instance of the BLEService - * @param [in] uuid The UUID of the service. - * @param [in] numHandles The maximum number of handles associated with the service. - */ -BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { -} - - -/** - * @brief Construct an instance of the BLEService - * @param [in] uuid The UUID of the service. - * @param [in] numHandles The maximum number of handles associated with the service. - */ -BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_pServer = nullptr; - //m_serializeMutex.setName("BLEService"); - m_lastCreatedCharacteristic = nullptr; - m_numHandles = numHandles; -} // BLEService - - -/** - * @brief Create the service. - * Create the service. - * @param [in] gatts_if The handle of the GATT server interface. - * @return N/A. - */ - -void BLEService::executeCreate(BLEServer *pServer) { - //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - //getUUID(); // Needed for a weird bug fix - //char x[1000]; - //memcpy(x, &m_uuid, sizeof(m_uuid)); - //char x[10]; - //memcpy(x, &deleteMe, 10); - m_pServer = pServer; - m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT - - esp_gatt_srvc_id_t srvc_id; - srvc_id.is_primary = true; - srvc_id.id.inst_id = m_id; - srvc_id.id.uuid = *m_uuid.getNative(); - esp_err_t errRc = ::esp_ble_gatts_create_service( - getServer()->getGattsIf(), - &srvc_id, - m_numHandles // The maximum number of handles associated with the service. - ); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreCreateEvt.wait("executeCreate"); - ESP_LOGD(LOG_TAG, "<< executeCreate"); -} // executeCreate - - -/** - * @brief Delete the service. - * Delete the service. - * @return N/A. - */ - -void BLEService::executeDelete() { - ESP_LOGD(LOG_TAG, ">> executeDelete()"); - m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT - - esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreDeleteEvt.wait("executeDelete"); - ESP_LOGD(LOG_TAG, "<< executeDelete"); -} // executeDelete - - -/** - * @brief Dump details of this BLE GATT service. - * @return N/A. - */ -void BLEService::dump() { - ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", - m_uuid.toString().c_str(), - m_handle); - ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); -} // dump - - -/** - * @brief Get the UUID of the service. - * @return the UUID of the service. - */ -BLEUUID BLEService::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Start the service. - * Here we wish to start the service which means that we will respond to partner requests about it. - * Starting a service also means that we can create the corresponding characteristics. - * @return Start the service. - */ -void BLEService::start() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// - ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); - return; - } - - - BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); - - while(pCharacteristic != nullptr) { - m_lastCreatedCharacteristic = pCharacteristic; - pCharacteristic->executeCreate(this); - - pCharacteristic = m_characteristicMap.getNext(); - } - // Start each of the characteristics ... these are found in the m_characteristicMap. - - m_semaphoreStartEvt.take("start"); - esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStartEvt.wait("start"); - - ESP_LOGD(LOG_TAG, "<< start()"); -} // start - - -/** - * @brief Stop the service. - * @return Stop the service. - */ -void BLEService::stop() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// - ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); - return; - } - - m_semaphoreStopEvt.take("stop"); - esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStopEvt.wait("stop"); - - ESP_LOGD(LOG_TAG, "<< stop()"); -} // start - - -/** - * @brief Set the handle associated with this service. - * @param [in] handle The handle associated with the service. - */ -void BLEService::setHandle(uint16_t handle) { - ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); - if (m_handle != NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); - return; - } - m_handle = handle; - ESP_LOGD(LOG_TAG, "<< setHandle"); -} // setHandle - - -/** - * @brief Get the handle associated with this service. - * @return The handle associated with this service. - */ -uint16_t BLEService::getHandle() { - return m_handle; -} // getHandle - - -/** - * @brief Add a characteristic to the service. - * @param [in] pCharacteristic A pointer to the characteristic to be added. - */ -void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { -// We maintain a mapping of characteristics owned by this service. These are managed by the -// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic -// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). -// - ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); - ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", - pCharacteristic->getUUID().toString().c_str(), - toString().c_str()); - - // Check that we don't add the same characteristic twice. - if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { - ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); - //return; - } - - // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID - // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. - m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); - - ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); -} // addCharacteristic - - -/** - * @brief Create a new BLE Characteristic associated with this service. - * @param [in] uuid - The UUID of the characteristic. - * @param [in] properties - The properties of the characteristic. - * @return The new BLE characteristic. - */ -BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { - return createCharacteristic(BLEUUID(uuid), properties); -} - - -/** - * @brief Create a new BLE Characteristic associated with this service. - * @param [in] uuid - The UUID of the characteristic. - * @param [in] properties - The properties of the characteristic. - * @return The new BLE characteristic. - */ -BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { - BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); - addCharacteristic(pCharacteristic); - return pCharacteristic; -} // createCharacteristic - - -/** - * @brief Handle a GATTS server event. - */ -void BLEService::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - - - switch(event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - - // If we have reached the correct service, then locate the characteristic and remember the handle - // for that characteristic. - case ESP_GATTS_ADD_CHAR_EVT: { - if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); - if (pCharacteristic == nullptr) { - ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", - BLEUUID(param->add_char.char_uuid).toString().c_str()); - dump(); - break; - } - pCharacteristic->setHandle(param->add_char.attr_handle); - m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); - //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); - break; - } // Reached the correct service. - break; - } // ESP_GATTS_ADD_CHAR_EVT - - - // ESP_GATTS_START_EVT - // - // start: - // esp_gatt_status_t status - // uint16_t service_handle - case ESP_GATTS_START_EVT: { - if (param->start.service_handle == getHandle()) { - m_semaphoreStartEvt.give(); - } - break; - } // ESP_GATTS_START_EVT - - // ESP_GATTS_STOP_EVT - // - // stop: - // esp_gatt_status_t status - // uint16_t service_handle - // - case ESP_GATTS_STOP_EVT: { - if (param->stop.service_handle == getHandle()) { - m_semaphoreStopEvt.give(); - } - break; - } // ESP_GATTS_STOP_EVT - - - // ESP_GATTS_CREATE_EVT - // Called when a new service is registered as having been created. - // - // create: - // * esp_gatt_status_t status - // * uint16_t service_handle - // * esp_gatt_srvc_id_t service_id - // * - esp_gatt_id id - // * - esp_bt_uuid uuid - // * - uint8_t inst_id - // * - bool is_primary - // - case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { - setHandle(param->create.service_handle); - m_semaphoreCreateEvt.give(); - } - break; - } // ESP_GATTS_CREATE_EVT - - - // ESP_GATTS_DELETE_EVT - // Called when a service is deleted. - // - // delete: - // * esp_gatt_status_t status - // * uint16_t service_handle - // - case ESP_GATTS_DELETE_EVT: { - if (param->del.service_handle == getHandle()) { - m_semaphoreDeleteEvt.give(); - } - break; - } // ESP_GATTS_DELETE_EVT - - default: { - break; - } // Default - } // Switch - - // Invoke the GATTS handler in each of the associated characteristics. - m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); -} // handleGATTServerEvent - - -BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { - return getCharacteristic(BLEUUID(uuid)); -} - - -BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { - return m_characteristicMap.getByUUID(uuid); -} - - -/** - * @brief Return a string representation of this service. - * A service is defined by: - * * Its UUID - * * Its handle - * @return A string representation of this service. - */ -std::string BLEService::toString() { - std::stringstream stringStream; - stringStream << "UUID: " << getUUID().toString() << - ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle(); - return stringStream.str(); -} // toString - - -/** - * @brief Get the last created characteristic. - * It is lamentable that this function has to exist. It returns the last created characteristic. - * We need this because the descriptor API is built around the notion that a new descriptor, when created, - * is associated with the last characteristics created and we need that information. - * @return The last created characteristic. - */ -BLECharacteristic* BLEService::getLastCreatedCharacteristic() { - return m_lastCreatedCharacteristic; -} // getLastCreatedCharacteristic - - -/** - * @brief Get the BLE server associated with this service. - * @return The BLEServer associated with this service. - */ -BLEServer* BLEService::getServer() { - return m_pServer; -} // getServer - -#endif // CONFIG_BT_ENABLED +/* + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include + +#include +#include +#include + +#include "BLEServer.h" +#include "BLEService.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" + +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +#define NULL_HANDLE (0xffff) + +static const char* LOG_TAG = "BLEService"; // Tag for logging. + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; + //m_serializeMutex.setName("BLEService"); + m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; +} // BLEService + + +/** + * @brief Create the service. + * Create the service. + * @param [in] gatts_if The handle of the GATT server interface. + * @return N/A. + */ + +void BLEService::executeCreate(BLEServer *pServer) { + //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); + //getUUID(); // Needed for a weird bug fix + //char x[1000]; + //memcpy(x, &m_uuid, sizeof(m_uuid)); + //char x[10]; + //memcpy(x, &deleteMe, 10); + m_pServer = pServer; + m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + + esp_gatt_srvc_id_t srvc_id; + srvc_id.is_primary = true; + srvc_id.id.inst_id = m_id; + srvc_id.id.uuid = *m_uuid.getNative(); + esp_err_t errRc = ::esp_ble_gatts_create_service( + getServer()->getGattsIf(), + &srvc_id, + m_numHandles // The maximum number of handles associated with the service. + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreCreateEvt.wait("executeCreate"); + ESP_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Delete the service. + * Delete the service. + * @return N/A. + */ + +void BLEService::executeDelete() { + ESP_LOGD(LOG_TAG, ">> executeDelete()"); + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT + + esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreDeleteEvt.wait("executeDelete"); + ESP_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void BLEService::dump() { + ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", + m_uuid.toString().c_str(), + m_handle); + ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +BLEUUID BLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Start the service. + * Here we wish to start the service which means that we will respond to partner requests about it. + * Starting a service also means that we can create the corresponding characteristics. + * @return Start the service. + */ +void BLEService::start() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); + return; + } + + + BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); + + while(pCharacteristic != nullptr) { + m_lastCreatedCharacteristic = pCharacteristic; + pCharacteristic->executeCreate(this); + + pCharacteristic = m_characteristicMap.getNext(); + } + // Start each of the characteristics ... these are found in the m_characteristicMap. + + m_semaphoreStartEvt.take("start"); + esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStartEvt.wait("start"); + + ESP_LOGD(LOG_TAG, "<< start()"); +} // start + + +/** + * @brief Stop the service. + * @return Stop the service. + */ +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start + + +/** + * @brief Set the handle associated with this service. + * @param [in] handle The handle associated with the service. + */ +void BLEService::setHandle(uint16_t handle) { + ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); + return; + } + m_handle = handle; + ESP_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t BLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Add a characteristic to the service. + * @param [in] pCharacteristic A pointer to the characteristic to be added. + */ +void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { +// We maintain a mapping of characteristics owned by this service. These are managed by the +// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic +// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). +// + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + pCharacteristic->getUUID().toString().c_str(), + toString().c_str()); + + // Check that we don't add the same characteristic twice. + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + //return; + } + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); + + ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); +} // addCharacteristic + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(BLEUUID(uuid), properties); +} + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { + BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); + addCharacteristic(pCharacteristic); + return pCharacteristic; +} // createCharacteristic + + +/** + * @brief Handle a GATTS server event. + */ +void BLEService::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) { + + + switch(event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + + // If we have reached the correct service, then locate the characteristic and remember the handle + // for that characteristic. + case ESP_GATTS_ADD_CHAR_EVT: { + if (m_handle == param->add_char.service_handle) { + BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); + if (pCharacteristic == nullptr) { + ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", + BLEUUID(param->add_char.char_uuid).toString().c_str()); + dump(); + break; + } + pCharacteristic->setHandle(param->add_char.attr_handle); + m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); + //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); + break; + } // Reached the correct service. + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_START_EVT + // + // start: + // esp_gatt_status_t status + // uint16_t service_handle + case ESP_GATTS_START_EVT: { + if (param->start.service_handle == getHandle()) { + m_semaphoreStartEvt.give(); + } + break; + } // ESP_GATTS_START_EVT + + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } + break; + } // ESP_GATTS_STOP_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // * - esp_gatt_id id + // * - esp_bt_uuid uuid + // * - uint8_t inst_id + // * - bool is_primary + // + case ESP_GATTS_CREATE_EVT: { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { + setHandle(param->create.service_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // + case ESP_GATTS_DELETE_EVT: { + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } + break; + } // ESP_GATTS_DELETE_EVT + + default: { + break; + } // Default + } // Switch + + // Invoke the GATTS handler in each of the associated characteristics. + m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); +} // handleGATTServerEvent + + +BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(BLEUUID(uuid)); +} + + +BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { + return m_characteristicMap.getByUUID(uuid); +} + + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string BLEService::toString() { + std::stringstream stringStream; + stringStream << "UUID: " << getUUID().toString() << + ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle(); + return stringStream.str(); +} // toString + + +/** + * @brief Get the last created characteristic. + * It is lamentable that this function has to exist. It returns the last created characteristic. + * We need this because the descriptor API is built around the notion that a new descriptor, when created, + * is associated with the last characteristics created and we need that information. + * @return The last created characteristic. + */ +BLECharacteristic* BLEService::getLastCreatedCharacteristic() { + return m_lastCreatedCharacteristic; +} // getLastCreatedCharacteristic + + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +BLEServer* BLEService::getServer() { + return m_pServer; +} // getServer + +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index d2ab0759..93b4b2c6 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -1,104 +1,104 @@ -/* - * BLEService.h - * - * Created on: Mar 25, 2017 - * Author: kolban - */ - -#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#define COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include - -#include "BLECharacteristic.h" -#include "BLEServer.h" -#include "BLEUUID.h" -#include "FreeRTOS.h" - -class BLEServer; - -/** - * @brief A data mapping used to manage the set of %BLE characteristics known to the server. - */ -class BLECharacteristicMap { -public: - void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); - void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); - void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); - BLECharacteristic* getByUUID(BLEUUID uuid); - BLECharacteristic* getByHandle(uint16_t handle); - BLECharacteristic* getFirst(); - BLECharacteristic* getNext(); - std::string toString(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - - -private: - std::map m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; -}; - - -/** - * @brief The model of a %BLE service. - * - */ -class BLEService { -public: - void addCharacteristic(BLECharacteristic* pCharacteristic); - BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); - BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); - void dump(); - void executeCreate(BLEServer* pServer); - void executeDelete(); - BLECharacteristic* getCharacteristic(const char* uuid); - BLECharacteristic* getCharacteristic(BLEUUID uuid); - BLEUUID getUUID(); - BLEServer* getServer(); - void start(); - void stop(); - std::string toString(); - uint16_t getHandle(); - uint8_t m_id = 0; - -private: - BLEService(const char* uuid, uint32_t numHandles); - BLEService(BLEUUID uuid, uint32_t numHandles); - friend class BLEServer; - friend class BLEServiceMap; - friend class BLEDescriptor; - friend class BLECharacteristic; - friend class BLEDevice; - - BLECharacteristicMap m_characteristicMap; - uint16_t m_handle; - BLECharacteristic* m_lastCreatedCharacteristic; - BLEServer* m_pServer; - BLEUUID m_uuid; - - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); - FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); - FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); - - uint32_t m_numHandles; - - BLECharacteristic* getLastCreatedCharacteristic(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - void setHandle(uint16_t handle); - //void setService(esp_gatt_srvc_id_t srvc_id); -}; // BLEService - - -#endif // CONFIG_BT_ENABLED -#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ +/* + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#define COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include "BLECharacteristic.h" +#include "BLEServer.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLEServer; + +/** + * @brief A data mapping used to manage the set of %BLE characteristics known to the server. + */ +class BLECharacteristicMap { +public: + void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); + void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); + BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(BLEUUID uuid); + BLECharacteristic* getByHandle(uint16_t handle); + BLECharacteristic* getFirst(); + BLECharacteristic* getNext(); + std::string toString(); + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + + +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE service. + * + */ +class BLEService { +public: + void addCharacteristic(BLECharacteristic* pCharacteristic); + BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); + BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); + void dump(); + void executeCreate(BLEServer* pServer); + void executeDelete(); + BLECharacteristic* getCharacteristic(const char* uuid); + BLECharacteristic* getCharacteristic(BLEUUID uuid); + BLEUUID getUUID(); + BLEServer* getServer(); + void start(); + void stop(); + std::string toString(); + uint16_t getHandle(); + uint8_t m_id = 0; + +private: + BLEService(const char* uuid, uint32_t numHandles); + BLEService(BLEUUID uuid, uint32_t numHandles); + friend class BLEServer; + friend class BLEServiceMap; + friend class BLEDescriptor; + friend class BLECharacteristic; + friend class BLEDevice; + + BLECharacteristicMap m_characteristicMap; + uint16_t m_handle; + BLECharacteristic* m_lastCreatedCharacteristic; + BLEServer* m_pServer; + BLEUUID m_uuid; + + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); + + uint32_t m_numHandles; + + BLECharacteristic* getLastCreatedCharacteristic(); + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + void setHandle(uint16_t handle); + //void setService(esp_gatt_srvc_id_t srvc_id); +}; // BLEService + + +#endif // CONFIG_BT_ENABLED +#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index 179de7a8..dd828fae 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -1,132 +1,132 @@ -/* - * BLEServiceMap.cpp - * - * Created on: Jun 22, 2017 - * Author: kolban - */ -#include "sdkconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include -#include "BLEService.h" - - -/** - * @brief Return the service by UUID. - * @param [in] UUID The UUID to look up the service. - * @return The characteristic. - */ -BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); -} - -/** - * @brief Return the service by UUID. - * @param [in] UUID The UUID to look up the service. - * @return The characteristic. - */ -BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { - for (auto &myPair : m_uuidMap) { - if (myPair.first->getUUID().equals(uuid)) { - return myPair.first; - } - } - //return m_uuidMap.at(uuid.toString()); - return nullptr; -} // getByUUID - - -/** - * @brief Return the service by handle. - * @param [in] handle The handle to look up the service. - * @return The service. - */ -BLEService* BLEServiceMap::getByHandle(uint16_t handle) { - return m_handleMap.at(handle); -} // getByHandle - - -/** - * @brief Set the service by UUID. - * @param [in] uuid The uuid of the service. - * @param [in] characteristic The service to cache. - * @return N/A. - */ -void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService *service) { - m_uuidMap.insert(std::pair(service, uuid.toString())); -} // setByUUID - - -/** - * @brief Set the service by handle. - * @param [in] handle The handle of the service. - * @param [in] service The service to cache. - * @return N/A. - */ -void BLEServiceMap::setByHandle(uint16_t handle, - BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); -} // setByHandle - - -/** - * @brief Return a string representation of the service map. - * @return A string representation of the service map. - */ -std::string BLEServiceMap::toString() { - std::stringstream stringStream; - stringStream << std::hex << std::setfill('0'); - for (auto &myPair: m_handleMap) { - stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; - } - return stringStream.str(); -} // toString - -void BLEServiceMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { - myPair.first->handleGATTServerEvent(event, gatts_if, param); - } -} - -/** - * @brief Get the first service in the map. - * @return The first service in the map. - */ -BLEService* BLEServiceMap::getFirst() { - m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEService* pRet = m_iterator->first; - m_iterator++; - return pRet; -} // getFirst - -/** - * @brief Get the next service in the map. - * @return The next service in the map. - */ -BLEService* BLEServiceMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEService* pRet = m_iterator->first; - m_iterator++; - return pRet; -} // getNext - -/** - * @brief Removes service from maps. - * @return N/A. - */ -void BLEServiceMap::removeService(BLEService *service){ - m_handleMap.erase(service->getHandle()); - m_uuidMap.erase(service); -} // removeService - -#endif /* CONFIG_BT_ENABLED */ +/* + * BLEServiceMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEService.h" + + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(const char* uuid) { + return getByUUID(BLEUUID(uuid)); +} + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the service by handle. + * @param [in] handle The handle to look up the service. + * @return The service. + */ +BLEService* BLEServiceMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Set the service by UUID. + * @param [in] uuid The uuid of the service. + * @param [in] characteristic The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByUUID(BLEUUID uuid, + BLEService *service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the service by handle. + * @param [in] handle The handle of the service. + * @param [in] service The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByHandle(uint16_t handle, + BLEService* service) { + m_handleMap.insert(std::pair(handle, service)); +} // setByHandle + + +/** + * @brief Return a string representation of the service map. + * @return A string representation of the service map. + */ +std::string BLEServiceMap::toString() { + std::stringstream stringStream; + stringStream << std::hex << std::setfill('0'); + for (auto &myPair: m_handleMap) { + stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; + } + return stringStream.str(); +} // toString + +void BLEServiceMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +BLEService* BLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +BLEService* BLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void BLEServiceMap::removeService(BLEService *service){ + m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 81d37c36..09d59d55 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -1,904 +1,904 @@ -/* - * WiFi.cpp - * - * Created on: Feb 25, 2017 - * Author: kolban - */ - -//#define _GLIBCXX_USE_C99 -#include -#include -#include -#include -#include "sdkconfig.h" - - -#include "WiFi.h" -#include -#include -#include -#include -#include -#include "GeneralUtils.h" -#include -#include -#include -#include -#include - -#include - - -static const char* LOG_TAG = "WiFi"; - - -/* -static void setDNSServer(char *ip) { - ip_addr_t dnsserver; - ESP_LOGD(tag, "Setting DNS[%d] to %s", 0, ip); - inet_pton(AF_INET, ip, &dnsserver); - ESP_LOGD(tag, "ip of DNS is %.8x", *(uint32_t *)&dnsserver); - dns_setserver(0, &dnsserver); -} -*/ - - -/** - * @brief Creates and uses a default event handler - */ -WiFi::WiFi() - : ip(0) - , gw(0) - , netmask(0) - , m_pWifiEventHandler(nullptr) -{ - m_eventLoopStarted = false; - m_initCalled = false; - //m_pWifiEventHandler = new WiFiEventHandler(); - m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? -} // WiFi - - -/** - * @brief Deletes the event handler that was used by the class - */ -WiFi::~WiFi() { - if (m_pWifiEventHandler != nullptr) { - delete m_pWifiEventHandler; - m_pWifiEventHandler = nullptr; - } -} // ~WiFi - - -/** - * @brief Add a reference to a DNS server. - * - * Here we define a server that will act as a DNS server. We can add two DNS - * servers in total. The first will be the primary, the second will be the backup. - * The public Google DNS servers are "8.8.8.8" and "8.8.4.4". - * - * For example: - * - * @code{.cpp} - * wifi.addDNSServer("8.8.8.8"); - * wifi.addDNSServer("8.8.4.4"); - * @endcode - * - * @param [in] ip The IP address of the DNS Server. - * @return N/A. - */ -void WiFi::addDNSServer(const std::string& ip) { - addDNSServer(ip.c_str()); -} // addDNSServer - - -void WiFi::addDNSServer(const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { - addDNSServer(ip); - } -} // addDNSServer - - -void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - init(); - ::dns_setserver(m_dnsCount, &ip); - m_dnsCount++; - m_dnsCount %= 2; -} // addDNSServer - - -/** - * @brief Set a reference to a DNS server. - * - * Here we define a server that will act as a DNS server. We use numdns to specify which DNS server to set - * - * For example: - * - * @code{.cpp} - * wifi.setDNSServer(0, "8.8.8.8"); - * wifi.setDNSServer(1, "8.8.4.4"); - * @endcode - * - * @param [in] numdns The DNS number we wish to set - * @param [in] ip The IP address of the DNS Server. - * @return N/A. - */ -void WiFi::setDNSServer(int numdns, const std::string& ip) { - setDNSServer(numdns, ip.c_str()); -} // setDNSServer - - -void WiFi::setDNSServer(int numdns, const char* ip) { - ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { - setDNSServer(numdns, dns_server); - } -} // setDNSServer - - -void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); - init(); - ::dns_setserver(numdns, &ip); -} // setDNSServer - - -/** - * @brief Connect to an external access point. - * - * The event handler will be called back with the outcome of the connection. - * - * @param [in] ssid The network SSID of the access point to which we wish to connect. - * @param [in] password The password of the access point to which we wish to connect. - * @param [in] waitForConnection Block until the connection has an outcome. - * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect - * @return ESP_OK if we are now connected and wifi_err_reason_t if not. - */ -uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ - ESP_LOGD(LOG_TAG, ">> connectAP"); - - m_apConnectionStatus = UINT8_MAX; - init(); - - if (ip != 0 && gw != 0 && netmask != 0) { - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client - - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; - - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } - - esp_err_t errRc = ::esp_wifi_set_mode(mode); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - wifi_config_t sta_config; - ::memset(&sta_config, 0, sizeof(sta_config)); - ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); - ::memcpy(sta_config.sta.password, password.data(), password.size()); - sta_config.sta.bssid_set = 0; - errRc = ::esp_wifi_set_config(WIFI_IF_STA, &sta_config); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. - do { - ESP_LOGD(LOG_TAG, "esp_wifi_connect"); - errRc = ::esp_wifi_connect(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - } - while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s - m_connectFinished.give(); - - ESP_LOGD(LOG_TAG, "<< connectAP"); - return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. -} // connectAP - - - -/** - * @brief Dump diagnostics to the log. - */ -void WiFi::dump() { - ESP_LOGD(LOG_TAG, "WiFi Dump"); - ESP_LOGD(LOG_TAG, "---------"); - char ipAddrStr[30]; - ip_addr_t ip = ::dns_getserver(0); - inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); - ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); -} // dump - - -/** - * @brief Returns whether wifi is connected to an access point - */ -bool WiFi::isConnectedToAP() { - return m_apConnectionStatus; -} // isConnected - - - -/** - * @brief Primary event handler interface. - */ -/* STATIC */ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { - // This is the common event handler that we have provided for all event processing. It is called for every event - // that is received by the WiFi subsystem. The "ctx" parameter is an instance of the current WiFi object that we are - // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this - // an indirection vector to the real caller. - - WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. - - // Invoke the event handler. - esp_err_t rc; - if (pWiFi->m_pWifiEventHandler != nullptr) { - rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); - } else { - rc = ESP_OK; - } - - // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that - // indicates we are waiting for a connection complete. - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. - pWiFi->m_apConnectionStatus = ESP_OK; - } else { - pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; - } - pWiFi->m_connectFinished.give(); - } - - return rc; -} // eventHandler - - -/** - * @brief Get the AP IP Info. - * @return The AP IP Info. - */ -tcpip_adapter_ip_info_t WiFi::getApIpInfo() { - //init(); - tcpip_adapter_ip_info_t ipInfo; - ::tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); - return ipInfo; -} // getApIpInfo - - -/** - * @brief Get the MAC address of the AP interface. - * @return The MAC address of the AP interface. - */ -std::string WiFi::getApMac() { - uint8_t mac[6]; - //init(); - esp_wifi_get_mac(WIFI_IF_AP, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); -} // getApMac - - -/** - * @brief Get the AP SSID. - * @return The AP SSID. - */ -std::string WiFi::getApSSID() { - wifi_config_t conf; - //init(); - esp_wifi_get_config(WIFI_IF_AP, &conf); - return std::string((char *)conf.sta.ssid); -} // getApSSID - -/** - * @brief Get the current ESP32 IP form AP. - * @return The ESP32 IP. - */ -std::string WiFi::getApIp(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaIp - -/** - * @brief Get the current AP netmask. - * @return The Netmask IP. - */ -std::string WiFi::getApNetmask(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaNetmask - -/** - * @brief Get the current AP Gateway IP. - * @return The Gateway IP. - */ -std::string WiFi::getApGateway(){ - tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaGateway - -/** - * @brief Lookup an IP address by host name. - * - * @param [in] hostName The hostname to resolve. - * - * @return The IP address of the host or 0.0.0.0 if not found. - */ -struct in_addr WiFi::getHostByName(const std::string& hostName) { - return getHostByName(hostName.c_str()); -} // getHostByName - - -struct in_addr WiFi::getHostByName(const char* hostName) { - struct in_addr retAddr; - struct hostent *he = gethostbyname(hostName); - if (he == nullptr) { - retAddr.s_addr = 0; - ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); - } else { - retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); - } - return retAddr; -} // getHostByName - - -/** - * @brief Get the WiFi Mode. - * @return The WiFi Mode. - */ -std::string WiFi::getMode() { - wifi_mode_t mode; - esp_wifi_get_mode(&mode); - switch(mode) { - case WIFI_MODE_NULL: - return "WIFI_MODE_NULL"; - case WIFI_MODE_STA: - return "WIFI_MODE_STA"; - case WIFI_MODE_AP: - return "WIFI_MODE_AP"; - case WIFI_MODE_APSTA: - return "WIFI_MODE_APSTA"; - default: - return "unknown"; - } -} // getMode - - -/** - * @brief Get the STA IP Info. - * @return The STA IP Info. - */ -tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { - tcpip_adapter_ip_info_t ipInfo; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - return ipInfo; -} // getStaIpInfo - -/** - * @brief Get the current ESP32 IP form STA. - * @return The ESP32 IP. - */ -std::string WiFi::getStaIp(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaIp - - -/** - * @brief Get the current STA netmask. - * @return The Netmask IP. - */ -std::string WiFi::getStaNetmask(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaNetmask - - -/** - * @brief Get the current STA Gateway IP. - * @return The Gateway IP. - */ -std::string WiFi::getStaGateway(){ - tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); - char ipAddrStr[30]; - inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); - return std::string(ipAddrStr); -} // getStaGateway - -/** - * @brief Get the MAC address of the STA interface. - * @return The MAC address of the STA interface. - */ -std::string WiFi::getStaMac() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - auto mac_str = (char*) malloc(18); - sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return std::string(std::move(mac_str)); -} // getStaMac - - -/** - * @brief Get the STA SSID. - * @return The STA SSID. - */ -std::string WiFi::getStaSSID() { - wifi_config_t conf; - esp_wifi_get_config(WIFI_IF_STA, &conf); - return std::string((char *)conf.ap.ssid); -} // getStaSSID - - -/** - * @brief Initialize WiFi. - */ -/* PRIVATE */ void WiFi::init() { - - // If we have already started the event loop, then change the handler otherwise - // start the event loop. - if (m_eventLoopStarted) { - esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. - } else { - esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - m_eventLoopStarted = true; - } - // Now, one way or another, the event handler is WiFi::eventHandler. - - if (!m_initCalled) { - ::nvs_flash_init(); - ::tcpip_adapter_init(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_err_t errRc = ::esp_wifi_init(&cfg); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - } - m_initCalled = true; -} // init - - -/** - * @brief Perform a WiFi scan looking for access points. - * - * An access point scan is performed and a vector of WiFi access point records - * is built and returned with one record per found scan instance. The scan is - * performed in a blocking fashion and will not return until the set of scanned - * access points has been built. - * - * @return A vector of WiFiAPRecord instances. - */ -std::vector WiFi::scan() { - ESP_LOGD(LOG_TAG, ">> scan"); - std::vector apRecords; - - init(); - - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - wifi_scan_config_t conf; - memset(&conf, 0, sizeof(conf)); - conf.show_hidden = true; - - esp_err_t rc = ::esp_wifi_scan_start(&conf, true); - if (rc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); - return apRecords; - } - - uint16_t apCount; // Number of access points available. - rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); - - wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); - if (list == nullptr) { - ESP_LOGE(LOG_TAG, "Failed to allocate memory"); - return apRecords; - } - - errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - for (auto i=0; i rhs.m_rssi;}); - return apRecords; -} // scan - - -/** - * @brief Start being an access point. - * - * @param[in] ssid The SSID to use to advertize for stations. - * @param[in] password The password to use for station connections. - * @param[in] auth The authorization mode for access to this access point. Options are: - * * WIFI_AUTH_OPEN - * * WIFI_AUTH_WPA_PSK - * * WIFI_AUTH_WPA2_PSK - * * WIFI_AUTH_WPA_WPA2_PSK - * * WIFI_AUTH_WPA2_ENTERPRISE - * * WIFI_AUTH_WEP - * @return N/A. - */ -void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { - startAP(ssid, password, auth, 0, false, 4); -} // startAP - -/** - * @brief Start being an access point. - * - * @param[in] ssid The SSID to use to advertize for stations. - * @param[in] password The password to use for station connections. - * @param[in] auth The authorization mode for access to this access point. Options are: - * * WIFI_AUTH_OPEN - * * WIFI_AUTH_WPA_PSK - * * WIFI_AUTH_WPA2_PSK - * * WIFI_AUTH_WPA_WPA2_PSK - * * WIFI_AUTH_WPA2_ENTERPRISE - * * WIFI_AUTH_WEP - * @param[in] channel from the access point. - * @param[in] is the ssid hidden, ore not. - * @param[in] limiting number of clients. - * @return N/A. - */ -void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { - ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); - - init(); - - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - // Build the apConfig structure. - wifi_config_t apConfig; - ::memset(&apConfig, 0, sizeof(apConfig)); - ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); - apConfig.ap.ssid_len = ssid.size(); - ::memcpy(apConfig.ap.password, password.data(), password.size()); - apConfig.ap.channel = channel; - apConfig.ap.authmode = auth; - apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; - apConfig.ap.max_connection = max_connection; - apConfig.ap.beacon_interval = 100; - - errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = ::esp_wifi_start(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - - errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - - ESP_LOGD(LOG_TAG, "<< startAP"); -} // startAP - -/** - * @brief Set the event handler to use to process detected events. - * @param[in] wifiEventHandler The class that will be used to process events. - */ -void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { - ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); - this->m_pWifiEventHandler = wifiEventHandler; - ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); -} // setWifiEventHandler - - -/** - * @brief Set the IP info and enable DHCP if ip != 0. If called with ip == 0 then DHCP is enabled. - * If called with bad values it will do nothing. - * - * Do not call this method if we are being an access point ourselves. - * - * For example, prior to calling `connectAP()` we could invoke: - * - * @code{.cpp} - * myWifi.setIPInfo("192.168.1.99", "192.168.1.1", "255.255.255.0"); - * @endcode - * - * @param [in] ip IP address value. - * @param [in] gw Gateway value. - * @param [in] netmask Netmask value. - * @return N/A. - */ -void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask) { - setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); -} // setIPInfo - - - -void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { - uint32_t new_ip; - uint32_t new_gw; - uint32_t new_netmask; - - auto success = (bool)inet_pton(AF_INET, ip, &new_ip); - success = success && inet_pton(AF_INET, gw, &new_gw); - success = success && inet_pton(AF_INET, netmask, &new_netmask); - - if(!success) { - return; - } - - setIPInfo(new_ip, new_gw, new_netmask); -} // setIPInfo - - -/** - * @brief Set the IP Info based on the IP address, gateway and netmask. - * @param [in] ip The IP address of our ESP32. - * @param [in] gw The gateway we should use. - * @param [in] netmask Our TCP/IP netmask value. - */ -void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { - init(); - - this->ip = ip; - this->gw = gw; - this->netmask = netmask; - - if(ip != 0 && gw != 0 && netmask != 0) { - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); - } else { - ip = 0; - ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); - } -} // setIPInfo - - -/** - * @brief Return a string representation of the WiFi access point record. - * - * @return A string representation of the WiFi access point record. - */ -std::string WiFiAPRecord::toString() { - std::string auth; - switch(getAuthMode()) { - case WIFI_AUTH_OPEN: - auth = "WIFI_AUTH_OPEN"; - break; - case WIFI_AUTH_WEP: - auth = "WIFI_AUTH_WEP"; - break; - case WIFI_AUTH_WPA_PSK: - auth = "WIFI_AUTH_WPA_PSK"; - break; - case WIFI_AUTH_WPA2_PSK: - auth = "WIFI_AUTH_WPA2_PSK"; - break; - case WIFI_AUTH_WPA_WPA2_PSK: - auth = "WIFI_AUTH_WPA_WPA2_PSK"; - break; - default: - auth = ""; - break; - } -// std::stringstream s; -// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; - auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); - sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); - return std::string(std::move(info_str)); -} // toString - -/* -MDNS::MDNS() { - esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} - -MDNS::~MDNS() { - if (m_mdns_server != nullptr) { - mdns_free(m_mdns_server); - } - m_mdns_server = nullptr; -} -*/ - -/** - * @brief Define the service for mDNS. - * - * @param [in] service - * @param [in] proto - * @param [in] port - * @return N/A. - */ -/* -void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { - serviceAdd(service.c_str(), proto.c_str(), port); -} // serviceAdd - - -void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { - serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); -} // serviceInstanceSet - - -void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { - servicePortSet(service.c_str(), proto.c_str(), port); -} // servicePortSet - - -void MDNS::serviceRemove(const std::string& service, const std::string& proto) { - serviceRemove(service.c_str(), proto.c_str()); -} // serviceRemove -*/ - -/** - * @brief Set the mDNS hostname. - * - * @param [in] hostname The host name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setHostname(const std::string& hostname) { - setHostname(hostname.c_str()); -} // setHostname -*/ - -/** - * @brief Set the mDNS instance. - * - * @param [in] instance The instance name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setInstance(const std::string& instance) { - setInstance(instance.c_str()); -} // setInstance -*/ - -/** - * @brief Define the service for mDNS. - * - * @param [in] service - * @param [in] proto - * @param [in] port - * @return N/A. - */ -/* -void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { - esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_add: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceAdd - - -void MDNS::serviceInstanceSet(const char* service, const char* proto, const char* instance) { - esp_err_t errRc = ::mdns_service_instance_set(m_mdns_server, service, proto, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_instance_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceInstanceSet - - -void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) { - esp_err_t errRc = ::mdns_service_port_set(m_mdns_server, service, proto, port); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_port_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // servicePortSet - - -void MDNS::serviceRemove(const char* service, const char* proto) { - esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // serviceRemove - -*/ -/** - * @brief Set the mDNS hostname. - * - * @param [in] hostname The host name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setHostname(const char* hostname) { - esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // setHostname -*/ - -/** - * @brief Set the mDNS instance. - * - * @param [in] instance The instance name to set against the mDNS. - * @return N/A. - */ -/* -void MDNS::setInstance(const char* instance) { - esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } -} // setInstance -*/ +/* + * WiFi.cpp + * + * Created on: Feb 25, 2017 + * Author: kolban + */ + +//#define _GLIBCXX_USE_C99 +#include +#include +#include +#include +#include "sdkconfig.h" + + +#include "WiFi.h" +#include +#include +#include +#include +#include +#include "GeneralUtils.h" +#include +#include +#include +#include +#include + +#include + + +static const char* LOG_TAG = "WiFi"; + + +/* +static void setDNSServer(char *ip) { + ip_addr_t dnsserver; + ESP_LOGD(tag, "Setting DNS[%d] to %s", 0, ip); + inet_pton(AF_INET, ip, &dnsserver); + ESP_LOGD(tag, "ip of DNS is %.8x", *(uint32_t *)&dnsserver); + dns_setserver(0, &dnsserver); +} +*/ + + +/** + * @brief Creates and uses a default event handler + */ +WiFi::WiFi() + : ip(0) + , gw(0) + , netmask(0) + , m_pWifiEventHandler(nullptr) +{ + m_eventLoopStarted = false; + m_initCalled = false; + //m_pWifiEventHandler = new WiFiEventHandler(); + m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? +} // WiFi + + +/** + * @brief Deletes the event handler that was used by the class + */ +WiFi::~WiFi() { + if (m_pWifiEventHandler != nullptr) { + delete m_pWifiEventHandler; + m_pWifiEventHandler = nullptr; + } +} // ~WiFi + + +/** + * @brief Add a reference to a DNS server. + * + * Here we define a server that will act as a DNS server. We can add two DNS + * servers in total. The first will be the primary, the second will be the backup. + * The public Google DNS servers are "8.8.8.8" and "8.8.4.4". + * + * For example: + * + * @code{.cpp} + * wifi.addDNSServer("8.8.8.8"); + * wifi.addDNSServer("8.8.4.4"); + * @endcode + * + * @param [in] ip The IP address of the DNS Server. + * @return N/A. + */ +void WiFi::addDNSServer(const std::string& ip) { + addDNSServer(ip.c_str()); +} // addDNSServer + + +void WiFi::addDNSServer(const char* ip) { + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + addDNSServer(ip); + } +} // addDNSServer + + +void WiFi::addDNSServer(ip_addr_t ip) { + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); + ::dns_setserver(m_dnsCount, &ip); + m_dnsCount++; + m_dnsCount %= 2; +} // addDNSServer + + +/** + * @brief Set a reference to a DNS server. + * + * Here we define a server that will act as a DNS server. We use numdns to specify which DNS server to set + * + * For example: + * + * @code{.cpp} + * wifi.setDNSServer(0, "8.8.8.8"); + * wifi.setDNSServer(1, "8.8.4.4"); + * @endcode + * + * @param [in] numdns The DNS number we wish to set + * @param [in] ip The IP address of the DNS Server. + * @return N/A. + */ +void WiFi::setDNSServer(int numdns, const std::string& ip) { + setDNSServer(numdns, ip.c_str()); +} // setDNSServer + + +void WiFi::setDNSServer(int numdns, const char* ip) { + ip_addr_t dns_server; + if(inet_pton(AF_INET, ip, &dns_server)) { + setDNSServer(numdns, dns_server); + } +} // setDNSServer + + +void WiFi::setDNSServer(int numdns, ip_addr_t ip) { + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + init(); + ::dns_setserver(numdns, &ip); +} // setDNSServer + + +/** + * @brief Connect to an external access point. + * + * The event handler will be called back with the outcome of the connection. + * + * @param [in] ssid The network SSID of the access point to which we wish to connect. + * @param [in] password The password of the access point to which we wish to connect. + * @param [in] waitForConnection Block until the connection has an outcome. + * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect + * @return ESP_OK if we are now connected and wifi_err_reason_t if not. + */ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ + ESP_LOGD(LOG_TAG, ">> connectAP"); + + m_apConnectionStatus = UINT8_MAX; + init(); + + if (ip != 0 && gw != 0 && netmask != 0) { + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client + + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } + + esp_err_t errRc = ::esp_wifi_set_mode(mode); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + wifi_config_t sta_config; + ::memset(&sta_config, 0, sizeof(sta_config)); + ::memcpy(sta_config.sta.ssid, ssid.data(), ssid.size()); + ::memcpy(sta_config.sta.password, password.data(), password.size()); + sta_config.sta.bssid_set = 0; + errRc = ::esp_wifi_set_config(WIFI_IF_STA, &sta_config); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. + do { + ESP_LOGD(LOG_TAG, "esp_wifi_connect"); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s + m_connectFinished.give(); + + ESP_LOGD(LOG_TAG, "<< connectAP"); + return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. +} // connectAP + + + +/** + * @brief Dump diagnostics to the log. + */ +void WiFi::dump() { + ESP_LOGD(LOG_TAG, "WiFi Dump"); + ESP_LOGD(LOG_TAG, "---------"); + char ipAddrStr[30]; + ip_addr_t ip = ::dns_getserver(0); + inet_ntop(AF_INET, &ip, ipAddrStr, sizeof(ipAddrStr)); + ESP_LOGD(LOG_TAG, "DNS Server[0]: %s", ipAddrStr); +} // dump + + +/** + * @brief Returns whether wifi is connected to an access point + */ +bool WiFi::isConnectedToAP() { + return m_apConnectionStatus; +} // isConnected + + + +/** + * @brief Primary event handler interface. + */ +/* STATIC */ esp_err_t WiFi::eventHandler(void* ctx, system_event_t* event) { + // This is the common event handler that we have provided for all event processing. It is called for every event + // that is received by the WiFi subsystem. The "ctx" parameter is an instance of the current WiFi object that we are + // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this + // an indirection vector to the real caller. + + WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. + + // Invoke the event handler. + esp_err_t rc; + if (pWiFi->m_pWifiEventHandler != nullptr) { + rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + } else { + rc = ESP_OK; + } + + // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that + // indicates we are waiting for a connection complete. + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { + + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. + pWiFi->m_apConnectionStatus = ESP_OK; + } else { + pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; + } + pWiFi->m_connectFinished.give(); + } + + return rc; +} // eventHandler + + +/** + * @brief Get the AP IP Info. + * @return The AP IP Info. + */ +tcpip_adapter_ip_info_t WiFi::getApIpInfo() { + //init(); + tcpip_adapter_ip_info_t ipInfo; + ::tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ipInfo); + return ipInfo; +} // getApIpInfo + + +/** + * @brief Get the MAC address of the AP interface. + * @return The MAC address of the AP interface. + */ +std::string WiFi::getApMac() { + uint8_t mac[6]; + //init(); + esp_wifi_get_mac(WIFI_IF_AP, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); +} // getApMac + + +/** + * @brief Get the AP SSID. + * @return The AP SSID. + */ +std::string WiFi::getApSSID() { + wifi_config_t conf; + //init(); + esp_wifi_get_config(WIFI_IF_AP, &conf); + return std::string((char *)conf.sta.ssid); +} // getApSSID + +/** + * @brief Get the current ESP32 IP form AP. + * @return The ESP32 IP. + */ +std::string WiFi::getApIp(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + +/** + * @brief Get the current AP netmask. + * @return The Netmask IP. + */ +std::string WiFi::getApNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + +/** + * @brief Get the current AP Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getApGateway(){ + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + +/** + * @brief Lookup an IP address by host name. + * + * @param [in] hostName The hostname to resolve. + * + * @return The IP address of the host or 0.0.0.0 if not found. + */ +struct in_addr WiFi::getHostByName(const std::string& hostName) { + return getHostByName(hostName.c_str()); +} // getHostByName + + +struct in_addr WiFi::getHostByName(const char* hostName) { + struct in_addr retAddr; + struct hostent *he = gethostbyname(hostName); + if (he == nullptr) { + retAddr.s_addr = 0; + ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); + } else { + retAddr = *(struct in_addr *)(he->h_addr_list[0]); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + } + return retAddr; +} // getHostByName + + +/** + * @brief Get the WiFi Mode. + * @return The WiFi Mode. + */ +std::string WiFi::getMode() { + wifi_mode_t mode; + esp_wifi_get_mode(&mode); + switch(mode) { + case WIFI_MODE_NULL: + return "WIFI_MODE_NULL"; + case WIFI_MODE_STA: + return "WIFI_MODE_STA"; + case WIFI_MODE_AP: + return "WIFI_MODE_AP"; + case WIFI_MODE_APSTA: + return "WIFI_MODE_APSTA"; + default: + return "unknown"; + } +} // getMode + + +/** + * @brief Get the STA IP Info. + * @return The STA IP Info. + */ +tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { + tcpip_adapter_ip_info_t ipInfo; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + return ipInfo; +} // getStaIpInfo + +/** + * @brief Get the current ESP32 IP form STA. + * @return The ESP32 IP. + */ +std::string WiFi::getStaIp(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + + +/** + * @brief Get the current STA netmask. + * @return The Netmask IP. + */ +std::string WiFi::getStaNetmask(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + + +/** + * @brief Get the current STA Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getStaGateway(){ + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + +/** + * @brief Get the MAC address of the STA interface. + * @return The MAC address of the STA interface. + */ +std::string WiFi::getStaMac() { + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + auto mac_str = (char*) malloc(18); + sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return std::string(std::move(mac_str)); +} // getStaMac + + +/** + * @brief Get the STA SSID. + * @return The STA SSID. + */ +std::string WiFi::getStaSSID() { + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + return std::string((char *)conf.ap.ssid); +} // getStaSSID + + +/** + * @brief Initialize WiFi. + */ +/* PRIVATE */ void WiFi::init() { + + // If we have already started the event loop, then change the handler otherwise + // start the event loop. + if (m_eventLoopStarted) { + esp_event_loop_set_cb(WiFi::eventHandler, this); // Returns the old handler. + } else { + esp_err_t errRc = ::esp_event_loop_init(WiFi::eventHandler, this); // Initialze the event handler. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_event_loop_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + m_eventLoopStarted = true; + } + // Now, one way or another, the event handler is WiFi::eventHandler. + + if (!m_initCalled) { + ::nvs_flash_init(); + ::tcpip_adapter_init(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t errRc = ::esp_wifi_init(&cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_storage: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + m_initCalled = true; +} // init + + +/** + * @brief Perform a WiFi scan looking for access points. + * + * An access point scan is performed and a vector of WiFi access point records + * is built and returned with one record per found scan instance. The scan is + * performed in a blocking fashion and will not return until the set of scanned + * access points has been built. + * + * @return A vector of WiFiAPRecord instances. + */ +std::vector WiFi::scan() { + ESP_LOGD(LOG_TAG, ">> scan"); + std::vector apRecords; + + init(); + + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + wifi_scan_config_t conf; + memset(&conf, 0, sizeof(conf)); + conf.show_hidden = true; + + esp_err_t rc = ::esp_wifi_scan_start(&conf, true); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); + return apRecords; + } + + uint16_t apCount; // Number of access points available. + rc = ::esp_wifi_scan_get_ap_num(&apCount); + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + + wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + if (list == nullptr) { + ESP_LOGE(LOG_TAG, "Failed to allocate memory"); + return apRecords; + } + + errRc = ::esp_wifi_scan_get_ap_records(&apCount, list); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_records: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + for (auto i=0; i rhs.m_rssi;}); + return apRecords; +} // scan + + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { + startAP(ssid, password, auth, 0, false, 4); +} // startAP + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @param[in] channel from the access point. + * @param[in] is the ssid hidden, ore not. + * @param[in] limiting number of clients. + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { + ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); + + init(); + + esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + // Build the apConfig structure. + wifi_config_t apConfig; + ::memset(&apConfig, 0, sizeof(apConfig)); + ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); + apConfig.ap.ssid_len = ssid.size(); + ::memcpy(apConfig.ap.password, password.data(), password.size()); + apConfig.ap.channel = channel; + apConfig.ap.authmode = auth; + apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; + apConfig.ap.max_connection = max_connection; + apConfig.ap.beacon_interval = 100; + + errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_set_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = ::esp_wifi_start(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + + errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + + ESP_LOGD(LOG_TAG, "<< startAP"); +} // startAP + +/** + * @brief Set the event handler to use to process detected events. + * @param[in] wifiEventHandler The class that will be used to process events. + */ +void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { + ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); + this->m_pWifiEventHandler = wifiEventHandler; + ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); +} // setWifiEventHandler + + +/** + * @brief Set the IP info and enable DHCP if ip != 0. If called with ip == 0 then DHCP is enabled. + * If called with bad values it will do nothing. + * + * Do not call this method if we are being an access point ourselves. + * + * For example, prior to calling `connectAP()` we could invoke: + * + * @code{.cpp} + * myWifi.setIPInfo("192.168.1.99", "192.168.1.1", "255.255.255.0"); + * @endcode + * + * @param [in] ip IP address value. + * @param [in] gw Gateway value. + * @param [in] netmask Netmask value. + * @return N/A. + */ +void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask) { + setIPInfo(ip.c_str(), gw.c_str(), netmask.c_str()); +} // setIPInfo + + + +void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { + uint32_t new_ip; + uint32_t new_gw; + uint32_t new_netmask; + + auto success = (bool)inet_pton(AF_INET, ip, &new_ip); + success = success && inet_pton(AF_INET, gw, &new_gw); + success = success && inet_pton(AF_INET, netmask, &new_netmask); + + if(!success) { + return; + } + + setIPInfo(new_ip, new_gw, new_netmask); +} // setIPInfo + + +/** + * @brief Set the IP Info based on the IP address, gateway and netmask. + * @param [in] ip The IP address of our ESP32. + * @param [in] gw The gateway we should use. + * @param [in] netmask Our TCP/IP netmask value. + */ +void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { + init(); + + this->ip = ip; + this->gw = gw; + this->netmask = netmask; + + if(ip != 0 && gw != 0 && netmask != 0) { + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + } else { + ip = 0; + ::tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); + } +} // setIPInfo + + +/** + * @brief Return a string representation of the WiFi access point record. + * + * @return A string representation of the WiFi access point record. + */ +std::string WiFiAPRecord::toString() { + std::string auth; + switch(getAuthMode()) { + case WIFI_AUTH_OPEN: + auth = "WIFI_AUTH_OPEN"; + break; + case WIFI_AUTH_WEP: + auth = "WIFI_AUTH_WEP"; + break; + case WIFI_AUTH_WPA_PSK: + auth = "WIFI_AUTH_WPA_PSK"; + break; + case WIFI_AUTH_WPA2_PSK: + auth = "WIFI_AUTH_WPA2_PSK"; + break; + case WIFI_AUTH_WPA_WPA2_PSK: + auth = "WIFI_AUTH_WPA_WPA2_PSK"; + break; + default: + auth = ""; + break; + } +// std::stringstream s; +// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; + auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); + sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); + return std::string(std::move(info_str)); +} // toString + +/* +MDNS::MDNS() { + esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} + +MDNS::~MDNS() { + if (m_mdns_server != nullptr) { + mdns_free(m_mdns_server); + } + m_mdns_server = nullptr; +} +*/ + +/** + * @brief Define the service for mDNS. + * + * @param [in] service + * @param [in] proto + * @param [in] port + * @return N/A. + */ +/* +void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { + serviceAdd(service.c_str(), proto.c_str(), port); +} // serviceAdd + + +void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { + serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); +} // serviceInstanceSet + + +void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { + servicePortSet(service.c_str(), proto.c_str(), port); +} // servicePortSet + + +void MDNS::serviceRemove(const std::string& service, const std::string& proto) { + serviceRemove(service.c_str(), proto.c_str()); +} // serviceRemove +*/ + +/** + * @brief Set the mDNS hostname. + * + * @param [in] hostname The host name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setHostname(const std::string& hostname) { + setHostname(hostname.c_str()); +} // setHostname +*/ + +/** + * @brief Set the mDNS instance. + * + * @param [in] instance The instance name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setInstance(const std::string& instance) { + setInstance(instance.c_str()); +} // setInstance +*/ + +/** + * @brief Define the service for mDNS. + * + * @param [in] service + * @param [in] proto + * @param [in] port + * @return N/A. + */ +/* +void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { + esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_add: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceAdd + + +void MDNS::serviceInstanceSet(const char* service, const char* proto, const char* instance) { + esp_err_t errRc = ::mdns_service_instance_set(m_mdns_server, service, proto, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_instance_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceInstanceSet + + +void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) { + esp_err_t errRc = ::mdns_service_port_set(m_mdns_server, service, proto, port); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_port_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // servicePortSet + + +void MDNS::serviceRemove(const char* service, const char* proto) { + esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // serviceRemove + +*/ +/** + * @brief Set the mDNS hostname. + * + * @param [in] hostname The host name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setHostname(const char* hostname) { + esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // setHostname +*/ + +/** + * @brief Set the mDNS instance. + * + * @param [in] instance The instance name to set against the mDNS. + * @return N/A. + */ +/* +void MDNS::setInstance(const char* instance) { + esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } +} // setInstance +*/ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index a4d5d4d5..c1b6bedc 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -1,158 +1,158 @@ -/* - * WiFi.h - * - * Created on: Feb 25, 2017 - * Author: kolban - */ - -#ifndef MAIN_WIFI_H_ -#define MAIN_WIFI_H_ -#include "sdkconfig.h" - -#include -#include -#include -#include -#include "FreeRTOS.h" -#include "WiFiEventHandler.h" - -/** - * @brief Manage mDNS server. - */ -/* -class MDNS { -public: - MDNS(); - ~MDNS(); - void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); - void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); - void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); - void serviceRemove(const std::string& service, const std::string& proto); - void setHostname(const std::string& hostname); - void setInstance(const std::string& instance); - // If we the above functions with a basic char*, a copy would be created into an std::string, - // making the whole thing require twice as much processing power and speed - void serviceAdd(const char* service, const char* proto, uint16_t port); - void serviceInstanceSet(const char* service, const char* proto, const char* instance); - void servicePortSet(const char* service, const char* proto, uint16_t port); - void serviceRemove(const char* service, const char* proto); - void setHostname(const char* hostname); - void setInstance(const char* instance); -private: - mdns_server_t *m_mdns_server = nullptr; -}; -*/ - -class WiFiAPRecord { -public: - friend class WiFi; - - /** - * @brief Get the auth mode. - * @return The auth mode. - */ - wifi_auth_mode_t getAuthMode() { - return m_authMode; - } - - /** - * @brief Get the RSSI. - * @return the RSSI. - */ - int8_t getRSSI() { - return m_rssi; - } - - /** - * @brief Get the SSID. - * @return the SSID. - */ - std::string getSSID() { - return m_ssid; - } - - std::string toString(); - -private: - uint8_t m_bssid[6]; - int8_t m_rssi; - std::string m_ssid; - wifi_auth_mode_t m_authMode; -}; - -/** - * @brief %WiFi driver. - * - * Encapsulate control of %WiFi functions. - * - * Here is an example fragment that illustrates connecting to an access point. - * @code{.cpp} - * #include - * #include - * - * class TargetWiFiEventHandler: public WiFiEventHandler { - * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { - * ESP_LOGD(tag, "MyWiFiEventHandler(Class): staGotIp"); - * // Do something ... - * return ESP_OK; - * } - * }; - * - * WiFi wifi; - * - * TargetWiFiEventHandler *eventHandler = new TargetWiFiEventHandler(); - * wifi.setWifiEventHandler(eventHandler); - * wifi.connectAP("myssid", "mypassword"); - * @endcode - */ -class WiFi { -private: - static esp_err_t eventHandler(void* ctx, system_event_t* event); - void init(); - uint32_t ip; - uint32_t gw; - uint32_t netmask; - WiFiEventHandler* m_pWifiEventHandler; - uint8_t m_dnsCount=0; - bool m_eventLoopStarted; - bool m_initCalled; - uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. - FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); - -public: - WiFi(); - ~WiFi(); - void addDNSServer(const std::string& ip); - void addDNSServer(const char* ip); - void addDNSServer(ip_addr_t ip); - void setDNSServer(int numdns, const std::string& ip); - void setDNSServer(int numdns, const char* ip); - void setDNSServer(int numdns, ip_addr_t ip); - struct in_addr getHostByName(const std::string& hostName); - struct in_addr getHostByName(const char* hostName); - uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); - void dump(); - bool isConnectedToAP(); - static std::string getApMac(); - static tcpip_adapter_ip_info_t getApIpInfo(); - static std::string getApSSID(); - static std::string getApIp(); - static std::string getApNetmask(); - static std::string getApGateway(); - static std::string getMode(); - static tcpip_adapter_ip_info_t getStaIpInfo(); - static std::string getStaMac(); - static std::string getStaSSID(); - static std::string getStaIp(); - static std::string getStaNetmask(); - static std::string getStaGateway(); - std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); - void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); - void setIPInfo(const char* ip, const char* gw, const char* netmask); - void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); -}; - -#endif /* MAIN_WIFI_H_ */ +/* + * WiFi.h + * + * Created on: Feb 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_WIFI_H_ +#define MAIN_WIFI_H_ +#include "sdkconfig.h" + +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "WiFiEventHandler.h" + +/** + * @brief Manage mDNS server. + */ +/* +class MDNS { +public: + MDNS(); + ~MDNS(); + void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); + void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); + void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); + void serviceRemove(const std::string& service, const std::string& proto); + void setHostname(const std::string& hostname); + void setInstance(const std::string& instance); + // If we the above functions with a basic char*, a copy would be created into an std::string, + // making the whole thing require twice as much processing power and speed + void serviceAdd(const char* service, const char* proto, uint16_t port); + void serviceInstanceSet(const char* service, const char* proto, const char* instance); + void servicePortSet(const char* service, const char* proto, uint16_t port); + void serviceRemove(const char* service, const char* proto); + void setHostname(const char* hostname); + void setInstance(const char* instance); +private: + mdns_server_t *m_mdns_server = nullptr; +}; +*/ + +class WiFiAPRecord { +public: + friend class WiFi; + + /** + * @brief Get the auth mode. + * @return The auth mode. + */ + wifi_auth_mode_t getAuthMode() { + return m_authMode; + } + + /** + * @brief Get the RSSI. + * @return the RSSI. + */ + int8_t getRSSI() { + return m_rssi; + } + + /** + * @brief Get the SSID. + * @return the SSID. + */ + std::string getSSID() { + return m_ssid; + } + + std::string toString(); + +private: + uint8_t m_bssid[6]; + int8_t m_rssi; + std::string m_ssid; + wifi_auth_mode_t m_authMode; +}; + +/** + * @brief %WiFi driver. + * + * Encapsulate control of %WiFi functions. + * + * Here is an example fragment that illustrates connecting to an access point. + * @code{.cpp} + * #include + * #include + * + * class TargetWiFiEventHandler: public WiFiEventHandler { + * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { + * ESP_LOGD(tag, "MyWiFiEventHandler(Class): staGotIp"); + * // Do something ... + * return ESP_OK; + * } + * }; + * + * WiFi wifi; + * + * TargetWiFiEventHandler *eventHandler = new TargetWiFiEventHandler(); + * wifi.setWifiEventHandler(eventHandler); + * wifi.connectAP("myssid", "mypassword"); + * @endcode + */ +class WiFi { +private: + static esp_err_t eventHandler(void* ctx, system_event_t* event); + void init(); + uint32_t ip; + uint32_t gw; + uint32_t netmask; + WiFiEventHandler* m_pWifiEventHandler; + uint8_t m_dnsCount=0; + bool m_eventLoopStarted; + bool m_initCalled; + uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. + FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); + +public: + WiFi(); + ~WiFi(); + void addDNSServer(const std::string& ip); + void addDNSServer(const char* ip); + void addDNSServer(ip_addr_t ip); + void setDNSServer(int numdns, const std::string& ip); + void setDNSServer(int numdns, const char* ip); + void setDNSServer(int numdns, ip_addr_t ip); + struct in_addr getHostByName(const std::string& hostName); + struct in_addr getHostByName(const char* hostName); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); + void dump(); + bool isConnectedToAP(); + static std::string getApMac(); + static tcpip_adapter_ip_info_t getApIpInfo(); + static std::string getApSSID(); + static std::string getApIp(); + static std::string getApNetmask(); + static std::string getApGateway(); + static std::string getMode(); + static tcpip_adapter_ip_info_t getStaIpInfo(); + static std::string getStaMac(); + static std::string getStaSSID(); + static std::string getStaIp(); + static std::string getStaNetmask(); + static std::string getStaGateway(); + std::vector scan(); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); + void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); + void setIPInfo(const char* ip, const char* gw, const char* netmask); + void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); + void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); +}; + +#endif /* MAIN_WIFI_H_ */ From 874ce79815817a4796612716c95ea6d639c9df0b Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 2 Oct 2018 01:37:06 +0200 Subject: [PATCH 252/310] move m_bleAdvertising to BLEDevice --- cpp_utils/BLEDevice.cpp | 18 ++++++++++++++++++ cpp_utils/BLEDevice.h | 3 +++ cpp_utils/BLEServer.cpp | 4 ++-- cpp_utils/BLEServer.h | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index a7db454b..157074d4 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -45,6 +45,7 @@ bool initialized = false; // Have we been initialized? esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; uint16_t BLEDevice::m_localMTU = 23; +BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; /** * @brief Create a new instance of a client. @@ -545,4 +546,21 @@ uint16_t BLEDevice::getMTU() { bool BLEDevice::getInitialized() { return initialized; } + +BLEAdvertising* BLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) + { + m_bleAdvertising = new BLEAdvertising(); + ESP_LOGI(LOG_TAG, "create advertising"); + } + ESP_LOGD(LOG_TAG, "get advertising"); + return m_bleAdvertising; +} + +void BLEDevice::startAdvertising() { + ESP_LOGD(LOG_TAG, ">> startAdvertising"); + getAdvertising()->start(); + ESP_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 7a1b833d..63183d0b 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -43,6 +43,8 @@ class BLEDevice { static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); static bool getInitialized(); // Returns the state of the device, is it initialized or not? + static BLEAdvertising* getAdvertising(); + static void startAdvertising(); private: static BLEServer *m_pServer; @@ -51,6 +53,7 @@ class BLEDevice { static esp_ble_sec_act_t m_securityLevel; static BLESecurityCallbacks* m_securityCallbacks; static uint16_t m_localMTU; + static BLEAdvertising *m_bleAdvertising; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 6a60f1db..1881c14b 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -120,7 +120,7 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @return An advertising object. */ BLEAdvertising* BLEServer::getAdvertising() { - return &m_bleAdvertising; + return BLEDevice::getAdvertising(); } uint16_t BLEServer::getConnId() { @@ -359,7 +359,7 @@ void BLEServer::removeService(BLEService *service) { */ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, ">> startAdvertising"); - m_bleAdvertising.start(); + BLEDevice::startAdvertising(); ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index a58b0e9b..7f40faef 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -73,7 +73,7 @@ class BLEServer { friend class BLEDevice; esp_ble_adv_data_t m_adv_data; uint16_t m_appId; - BLEAdvertising m_bleAdvertising; + // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint32_t m_connectedCount; uint16_t m_gatts_if; From 482f7b4062893095650167e9feb7f85663459e66 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 2 Oct 2018 19:45:12 +0200 Subject: [PATCH 253/310] some small bugfixes --- cpp_utils/BLEAdvertisedDevice.cpp | 2 +- cpp_utils/BLERemoteService.cpp | 14 ++++++++++++-- .../BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino | 8 ++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 67603dfb..d2ba1d91 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -137,7 +137,7 @@ BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is * @return Return true if service is advertised */ bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){ - for (int i = 0; i < m_serviceUUIDs.size(); ++i) { + for (int i = 0; i < m_serviceUUIDs.size(); i++) { if(m_serviceUUIDs[i].equals(uuid)) return true; } diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 78ff9914..ef0588bc 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -114,7 +114,7 @@ void BLERemoteService::gattClientEventHandler( } // switch // Send the event to each of the characteristics owned by this service. - for (auto &myPair : m_characteristicMap) { + for (auto &myPair : m_characteristicMapByHandle) { myPair.second->gattClientEventHandler(event, gattc_if, evtParam); } } // gattClientEventHandler @@ -206,7 +206,7 @@ void BLERemoteService::retrieveCharacteristics() { ); m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); - + m_characteristicMapByHandle.insert(std::pair(result.char_handle, pNewRemoteCharacteristic)); offset++; // Increment our count of number of descriptors found. } // Loop forever (until we break inside the loop). @@ -231,6 +231,12 @@ std::map * BLERemoteService::getCharacte return &m_characteristicMap; } // getCharacteristics +/** + * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID + */ +void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap){ + pCharacteristicMap = &m_characteristicMapByHandle; +} // Get the characteristics map. /** * @brief Get the client associated with this service. @@ -292,6 +298,10 @@ void BLERemoteService::removeCharacteristics() { //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear } m_characteristicMap.clear(); // Clear the map + for (auto &myPair : m_characteristicMapByHandle) { + delete myPair.second; + } + m_characteristicMapByHandle.clear(); // Clear the map } // removeCharacteristics diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino index 5f6ed002..2c2ec6a3 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino @@ -49,7 +49,7 @@ void setBeacon() { BLEBeacon oBeacon = BLEBeacon(); oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) - oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); oBeacon.setMinor(bootcount&0xFFFF); BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); @@ -84,10 +84,10 @@ void setup() { // Create the BLE Device BLEDevice::init(""); - // Create the BLE Server - BLEServer *pServer = BLEDevice::createServer(); + // Create the BLE Server is no longer required to crete beacon + // BLEServer *pServer = BLEDevice::createServer(); - pAdvertising = pServer->getAdvertising(); + pAdvertising = BLEDevice::getAdvertising(); setBeacon(); // Start advertising From b1ef951052c3be844ee3a9a1be8254b3b4fa6f2c Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 4 Oct 2018 00:44:26 +0200 Subject: [PATCH 254/310] fix macOS compatibility --- cpp_utils/BLEHIDDevice.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index 29376f3a..944d179e 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -111,10 +111,14 @@ void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + BLE2902* p2902 = new BLE2902(); + inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); uint8_t desc1_val[] = {reportID, 0x01}; inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); - inputReportCharacteristic->addDescriptor(new BLE2902()); + inputReportCharacteristic->addDescriptor(p2902; inputReportCharacteristic->addDescriptor(inputReportDescriptor); return inputReportCharacteristic; @@ -128,6 +132,8 @@ BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); uint8_t desc1_val[] = {reportID, 0x02}; outputReportDescriptor->setValue((uint8_t*)desc1_val, 2); @@ -144,6 +150,8 @@ BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); uint8_t desc1_val[] = {reportID, 0x03}; featureReportDescriptor->setValue((uint8_t*)desc1_val, 2); From bac1b24dee019a14949509b67155be1797b59757 Mon Sep 17 00:00:00 2001 From: chegewara Date: Thu, 4 Oct 2018 00:50:27 +0200 Subject: [PATCH 255/310] fix macOS HID compatibility --- cpp_utils/BLEHIDDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index 944d179e..cc6c531d 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -118,7 +118,7 @@ BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { uint8_t desc1_val[] = {reportID, 0x01}; inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); - inputReportCharacteristic->addDescriptor(p2902; + inputReportCharacteristic->addDescriptor(p2902); inputReportCharacteristic->addDescriptor(inputReportDescriptor); return inputReportCharacteristic; From b17e34656bc5e712096ec1e2a3bd2198f23f9641 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:27:09 -0600 Subject: [PATCH 256/310] Common code style --- cpp_utils/Apa102.cpp | 2 +- cpp_utils/BLE2902.cpp | 17 +- cpp_utils/BLE2904.cpp | 14 +- cpp_utils/BLEAddress.cpp | 29 +- cpp_utils/BLEAdvertisedDevice.cpp | 32 +- cpp_utils/BLEAdvertisedDevice.h | 3 +- cpp_utils/BLEAdvertising.cpp | 50 +- cpp_utils/BLEAdvertising.h | 1 - cpp_utils/BLEBeacon.cpp | 2 +- cpp_utils/BLEBeacon.h | 2 +- cpp_utils/BLECharacteristic.cpp | 105 +-- cpp_utils/BLECharacteristic.h | 70 +- cpp_utils/BLECharacteristicMap.cpp | 18 +- cpp_utils/BLEClient.cpp | 11 +- cpp_utils/BLEClient.h | 5 +- cpp_utils/BLEDescriptor.cpp | 15 +- cpp_utils/BLEDescriptorMap.cpp | 33 +- cpp_utils/BLEDevice.cpp | 137 ++- cpp_utils/BLEDevice.h | 12 +- cpp_utils/BLEEddystoneTLM.cpp | 50 +- cpp_utils/BLEEddystoneTLM.h | 41 +- cpp_utils/BLEEddystoneURL.cpp | 133 ++- cpp_utils/BLEEddystoneURL.h | 29 +- cpp_utils/BLEExceptions.h | 4 +- cpp_utils/BLEHIDDevice.cpp | 63 +- cpp_utils/BLEHIDDevice.h | 18 +- cpp_utils/BLERemoteCharacteristic.cpp | 93 +- cpp_utils/BLERemoteCharacteristic.h | 25 +- cpp_utils/BLERemoteDescriptor.cpp | 31 +- cpp_utils/BLERemoteService.cpp | 20 +- cpp_utils/BLERemoteService.h | 5 +- cpp_utils/BLEScan.cpp | 18 +- cpp_utils/BLEScan.h | 2 +- cpp_utils/BLESecurity.cpp | 45 +- cpp_utils/BLESecurity.h | 1 + cpp_utils/BLEServer.cpp | 33 +- cpp_utils/BLEServer.h | 12 +- cpp_utils/BLEService.cpp | 52 +- cpp_utils/BLEService.h | 20 +- cpp_utils/BLEServiceMap.cpp | 22 +- cpp_utils/BLEUUID.cpp | 86 +- cpp_utils/BLEUtils.cpp | 455 +++------ cpp_utils/BLEUtils.h | 6 +- cpp_utils/BLEValue.cpp | 13 +- cpp_utils/BLEValue.h | 15 +- cpp_utils/BLEXML/Characteristics/code/run.sh | 4 +- cpp_utils/BLEXML/Services/code/run.sh | 4 +- cpp_utils/CPPNVS.cpp | 2 +- cpp_utils/CPPNVS.h | 8 +- cpp_utils/Console.cpp | 27 +- cpp_utils/Console.h | 16 +- cpp_utils/FTPCallbacks.cpp | 13 +- cpp_utils/FTPServer.cpp | 116 +-- cpp_utils/FTPServer.h | 89 +- cpp_utils/File.cpp | 34 +- cpp_utils/File.h | 2 +- cpp_utils/FileSystem.cpp | 43 +- cpp_utils/FreeRTOS.cpp | 29 +- cpp_utils/FreeRTOS.h | 9 +- cpp_utils/FreeRTOSTimer.cpp | 17 +- cpp_utils/FreeRTOSTimer.h | 17 +- cpp_utils/GPIO.cpp | 22 +- cpp_utils/GPIO.h | 3 +- cpp_utils/GeneralUtils.cpp | 182 ++-- cpp_utils/GeneralUtils.h | 2 +- cpp_utils/HIDKeyboardTypes.h | 58 +- cpp_utils/HIDTypes.h | 4 +- cpp_utils/HttpParser.cpp | 56 +- cpp_utils/HttpParser.h | 30 +- cpp_utils/HttpRequest.cpp | 37 +- cpp_utils/HttpRequest.h | 13 +- cpp_utils/HttpResponse.cpp | 32 +- cpp_utils/HttpResponse.h | 20 +- cpp_utils/HttpServer.cpp | 50 +- cpp_utils/HttpServer.h | 2 +- cpp_utils/I2C.cpp | 25 +- cpp_utils/I2C.h | 25 +- cpp_utils/I2S.cpp | 181 ++-- cpp_utils/IFTTT.cpp | 2 +- cpp_utils/JSON.cpp | 55 +- cpp_utils/JSON.h | 7 +- cpp_utils/MAX7219.cpp | 106 +-- cpp_utils/MAX7219.h | 43 +- cpp_utils/MFRC522.cpp | 563 +++++------- cpp_utils/MFRC522.h | 46 +- cpp_utils/MFRC522Debug.h | 3 +- cpp_utils/MMU.cpp | 37 +- cpp_utils/MPU6050.cpp | 13 +- cpp_utils/MPU6050.h | 34 +- cpp_utils/MRFC522Debug.cpp | 40 +- cpp_utils/Makefile.arduino | 1 - cpp_utils/Memory.cpp | 23 +- cpp_utils/Memory.h | 1 - cpp_utils/NeoPixelWiFiEventHandler.h | 3 +- cpp_utils/OV7670.cpp | 229 +++-- cpp_utils/OV7670.h | 4 +- cpp_utils/PCF8574.cpp | 11 +- cpp_utils/PCF8574.h | 3 +- cpp_utils/PCF8575.cpp | 20 +- cpp_utils/PCF8575.h | 2 +- cpp_utils/PWM.cpp | 11 +- cpp_utils/PWM.h | 4 +- cpp_utils/PubSubClient.cpp | 248 ++--- cpp_utils/PubSubClient.h | 48 +- cpp_utils/RESTClient.cpp | 26 +- cpp_utils/RESTClient.h | 17 +- cpp_utils/RMT.cpp | 16 +- cpp_utils/RMT.h | 4 +- cpp_utils/SOC.cpp | 8 +- cpp_utils/SPI.cpp | 5 +- cpp_utils/SPI.h | 45 +- cpp_utils/SSLUtils.cpp | 4 +- cpp_utils/SmartLED.cpp | 130 ++- cpp_utils/SmartLED.h | 3 +- cpp_utils/SockServ.cpp | 25 +- cpp_utils/SockServ.h | 3 +- cpp_utils/Socket.cpp | 244 +++-- cpp_utils/Socket.h | 30 +- cpp_utils/System.cpp | 17 +- cpp_utils/System.h | 2 +- cpp_utils/TFTP.cpp | 88 +- cpp_utils/TFTP.h | 8 +- cpp_utils/Task.cpp | 16 +- cpp_utils/Task.h | 9 +- cpp_utils/U8G2.h | 13 +- cpp_utils/WS2812.cpp | 154 ++-- cpp_utils/WS2812.h | 12 +- cpp_utils/WebServer.cpp | 919 +++++++++---------- cpp_utils/WebServer.h | 365 ++++---- cpp_utils/WebSocket.cpp | 63 +- cpp_utils/WebSocket.h | 32 +- cpp_utils/WebSocketFileTransfer.cpp | 33 +- cpp_utils/WebSocketFileTransfer.h | 2 +- cpp_utils/WiFi.cpp | 188 ++-- cpp_utils/WiFi.h | 174 ++-- cpp_utils/WiFiEventHandler.cpp | 72 +- cpp_utils/WiFiEventHandler.h | 4 +- cpp_utils/mainpage.dox | 2 +- 138 files changed, 3402 insertions(+), 3910 deletions(-) diff --git a/cpp_utils/Apa102.cpp b/cpp_utils/Apa102.cpp index 274ec83f..c35c9150 100644 --- a/cpp_utils/Apa102.cpp +++ b/cpp_utils/Apa102.cpp @@ -37,7 +37,7 @@ void Apa102::show() { double brigthnessScale = getBrightness() / 100.0; // Loop over all the pixels in the pixels array to set the colors. - for (int i=0; i(payload), length)); break; @@ -274,16 +273,16 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { case ESP_BLE_AD_TYPE_16SRV_CMPL: case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 - for (int var = 0; var < length/2; ++var) { - setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*2))); + for (int var = 0; var < length / 2; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 2))); } break; } // ESP_BLE_AD_TYPE_16SRV_PART case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 - for (int var = 0; var < length/4; ++var) { - setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4))); + for (int var = 0; var < length / 4; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 4))); } break; } // ESP_BLE_AD_TYPE_32SRV_PART @@ -309,10 +308,10 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); break; } - uint16_t uuid = *(uint16_t *)payload; + uint16_t uuid = *(uint16_t*) payload; setServiceDataUUID(BLEUUID(uuid)); if (length > 2) { - setServiceData(std::string(reinterpret_cast(payload+2), length-2)); + setServiceData(std::string(reinterpret_cast(payload + 2), length - 2)); } break; } //ESP_BLE_AD_TYPE_SERVICE_DATA @@ -322,10 +321,10 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); break; } - uint32_t uuid = *(uint32_t *)payload; + uint32_t uuid = *(uint32_t*) payload; setServiceDataUUID(BLEUUID(uuid)); if (length > 4) { - setServiceData(std::string(reinterpret_cast(payload+4), length-4)); + setServiceData(std::string(reinterpret_cast(payload + 4), length - 4)); } break; } //ESP_BLE_AD_TYPE_32SERVICE_DATA @@ -336,9 +335,9 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } - setServiceDataUUID(BLEUUID(payload, (size_t)16, false)); + setServiceDataUUID(BLEUUID(payload, (size_t) 16, false)); if (length > 16) { - setServiceData(std::string(reinterpret_cast(payload+16), length-16)); + setServiceData(std::string(reinterpret_cast(payload + 16), length - 16)); } break; } //ESP_BLE_AD_TYPE_32SERVICE_DATA @@ -352,7 +351,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { } // Length <> 0 - if (sizeConsumed >=31 || length == 0) { + if (sizeConsumed >= 31 || length == 0) { finished = true; } } // !finished @@ -395,7 +394,7 @@ void BLEAdvertisedDevice::setAppearance(uint16_t appearance) { void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) { m_manufacturerData = manufacturerData; m_haveManufacturerData = true; - char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)m_manufacturerData.data(), (uint8_t)m_manufacturerData.length()); + char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length()); ESP_LOGD(LOG_TAG, "- manufacturer data: %s", pHex); free(pHex); } // setManufacturerData @@ -517,4 +516,3 @@ void BLEAdvertisedDevice::setPayload(uint8_t* payload) { #endif /* CONFIG_BT_ENABLED */ - diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index a3b1e6e2..eebdd6ef 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -72,7 +72,6 @@ class BLEAdvertisedDevice { void setTXPower(int8_t txPower); void setPayload(uint8_t* payload); - bool m_haveAppearance; bool m_haveManufacturerData; bool m_haveName; @@ -82,7 +81,6 @@ class BLEAdvertisedDevice { bool m_haveTXPower; - BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); uint8_t m_adFlag; uint16_t m_appearance; int m_deviceType; @@ -95,6 +93,7 @@ class BLEAdvertisedDevice { std::string m_serviceData; BLEUUID m_serviceDataUUID; uint8_t* m_payload; + BLEAddress m_address = BLEAddress((uint8_t*) "\0\0\0\0\0\0"); }; /** diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 4b3cb8d6..ea3825ac 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -107,7 +107,7 @@ void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ -void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; @@ -173,21 +173,20 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); - // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) // representations. If we have 1 or more services to advertise then we allocate enough // storage to host them and then copy them in one at a time into the contiguous storage. int numServices = m_serviceUUIDs.size(); if (numServices > 0) { - m_advData.service_uuid_len = 16*numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + m_advData.service_uuid_len = 16 * numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; uint8_t* p = m_advData.p_service_uuid; - for (int i=0; iuuid.uuid128, 16); - p+=16; + p += 16; } } else { m_advData.service_uuid_len = 0; @@ -196,8 +195,8 @@ void BLEAdvertising::start() { esp_err_t errRc; - if (m_customAdvData == false) { - // Set the configuration for advertising. + if (!m_customAdvData) { + // Set the configuration for advertising. m_advData.set_scan_rsp = false; errRc = ::esp_ble_gap_config_adv_data(&m_advData); if (errRc != ESP_OK) { @@ -206,7 +205,7 @@ void BLEAdvertising::start() { } } - if (m_customScanResponseData == false) { + if (!m_customScanResponseData) { m_advData.set_scan_rsp = true; errRc = ::esp_ble_gap_config_adv_data(&m_advData); if (errRc != ESP_OK) { @@ -270,7 +269,7 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); } // setAppearance @@ -280,12 +279,12 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { */ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); break; } @@ -293,7 +292,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); break; } @@ -301,7 +300,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16)); break; } @@ -341,7 +340,7 @@ void BLEAdvertisementData::setManufacturerData(std::string data) { char cdata[2]; cdata[0] = data.length() + 1; cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff - addData(std::string(cdata, 2) + data); + addData(std::string(cdata, 2) + data); ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData @@ -355,7 +354,7 @@ void BLEAdvertisementData::setName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setName"); } // setName @@ -366,12 +365,12 @@ void BLEAdvertisementData::setName(std::string name) { */ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); break; } @@ -379,7 +378,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); break; } @@ -387,7 +386,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); break; } @@ -404,12 +403,12 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { */ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x16] [UUID16] data cdata[0] = data.length() + 3; cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); break; } @@ -417,7 +416,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x20] [UUID32] data cdata[0] = data.length() + 5; cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); break; } @@ -425,7 +424,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x21] [UUID128] data cdata[0] = data.length() + 17; cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); break; } @@ -444,12 +443,11 @@ void BLEAdvertisementData::setShortName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setShortName"); } // setShortName - /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index e3165298..f7cfc240 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -19,7 +19,6 @@ class BLEAdvertisementData { // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will // be exposed on demand/request or as time permits. - // public: void setAppearance(uint16_t appearance); void setCompleteServices(BLEUUID uuid); diff --git a/cpp_utils/BLEBeacon.cpp b/cpp_utils/BLEBeacon.cpp index a63197ca..854b661d 100644 --- a/cpp_utils/BLEBeacon.cpp +++ b/cpp_utils/BLEBeacon.cpp @@ -25,7 +25,7 @@ BLEBeacon::BLEBeacon() { } // BLEBeacon std::string BLEBeacon::getData() { - return std::string((char*)&m_beaconData, sizeof(m_beaconData)); + return std::string((char*) &m_beaconData, sizeof(m_beaconData)); } // getData uint16_t BLEBeacon::getMajor() { diff --git a/cpp_utils/BLEBeacon.h b/cpp_utils/BLEBeacon.h index 0b02e2bd..277bd670 100644 --- a/cpp_utils/BLEBeacon.h +++ b/cpp_utils/BLEBeacon.h @@ -23,7 +23,7 @@ class BLEBeacon { uint16_t major; uint16_t minor; int8_t signalPower; - } __attribute__((packed))m_beaconData; + } __attribute__((packed)) m_beaconData; public: BLEBeacon(); std::string getData(); diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 56a27ddb..6f696c7c 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -44,15 +44,15 @@ BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BL BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { m_bleUUID = uuid; m_handle = NULL_HANDLE; - m_properties = (esp_gatt_char_prop_t)0; + m_properties = (esp_gatt_char_prop_t) 0; m_pCallbacks = nullptr; - setBroadcastProperty((properties & PROPERTY_BROADCAST) !=0); - setReadProperty((properties & PROPERTY_READ) !=0); - setWriteProperty((properties & PROPERTY_WRITE) !=0); - setNotifyProperty((properties & PROPERTY_NOTIFY) !=0); - setIndicateProperty((properties & PROPERTY_INDICATE) !=0); - setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) !=0); + setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); + setReadProperty((properties & PROPERTY_READ) != 0); + setWriteProperty((properties & PROPERTY_WRITE) != 0); + setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); + setIndicateProperty((properties & PROPERTY_INDICATE) != 0); + setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); } // BLECharacteristic /** @@ -216,7 +216,7 @@ void BLECharacteristic::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - switch(event) { + switch (event) { // Events handled: // // ESP_GATTS_ADD_CHAR_EVT @@ -267,7 +267,7 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_ADD_CHAR_EVT: { if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && getHandle() == param->add_char.attr_handle && - getService()->getHandle()==param->add_char.service_handle) { + getService()->getHandle() == param->add_char.service_handle) { m_semaphoreCreateEvt.give(); } break; @@ -357,7 +357,7 @@ void BLECharacteristic::handleGATTServerEvent( // // If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. // If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than -// 22 bytes, then we "just" send it and thats the end of the story. +// 22 bytes, then we "just" send it and that's the end of the story. // If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. // If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. // Because of follow on request processing, we need to maintain an offset of how much data we have already sent @@ -367,12 +367,10 @@ void BLECharacteristic::handleGATTServerEvent( // // The following code has deliberately not been factored to make it fewer statements because this would cloud the // the logic flow comprehension. -// + // TODO requires some more research to confirm that 512 is max PDU like in bluetooth specs uint16_t maxOffset = BLEDevice::getMTU() - 1; - if (BLEDevice::getMTU() > 512) { - maxOffset = 512; - } + if (BLEDevice::getMTU() > 512) maxOffset = 512; if (param->read.need_rsp) { ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; @@ -401,7 +399,7 @@ void BLECharacteristic::handleGATTServerEvent( std::string value = m_value.getValue(); - if (value.length()+1 > maxOffset) { + if (value.length() + 1 > maxOffset) { // Too big for a single shot entry. m_value.setReadOffset(maxOffset); rsp.attr_value.len = maxOffset; @@ -477,7 +475,6 @@ void BLECharacteristic::handleGATTServerEvent( * @return N/A */ void BLECharacteristic::indicate() { - ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); assert(getService() != nullptr); @@ -493,7 +490,7 @@ void BLECharacteristic::indicate() { // Test to see if we have a 0x2902 descriptor. If we do, then check to see if indications are enabled // and, if not, prevent the indication. - BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + BLE2902 *p2902 = (BLE2902*) getDescriptorByUUID((uint16_t) 0x2902); if (p2902 != nullptr && !p2902->getIndications()) { ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); return; @@ -531,11 +528,9 @@ void BLECharacteristic::indicate() { void BLECharacteristic::notify() { ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); - assert(getService() != nullptr); assert(getService()->getServer() != nullptr); - GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); if (getService()->getServer()->getConnectedCount() == 0) { @@ -546,7 +541,7 @@ void BLECharacteristic::notify() { // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled // and, if not, prevent the notification. - BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + BLE2902 *p2902 = (BLE2902*) getDescriptorByUUID((uint16_t) 0x2902); if (p2902 != nullptr && !p2902->getNotifications()) { ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); return; @@ -585,9 +580,9 @@ void BLECharacteristic::notify() { void BLECharacteristic::setBroadcastProperty(bool value) { //ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); } } // setBroadcastProperty @@ -597,7 +592,7 @@ void BLECharacteristic::setBroadcastProperty(bool value) { * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. */ void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { - ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks); + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallbacks); m_pCallbacks = pCallbacks; ESP_LOGD(LOG_TAG, "<< setCallbacks"); } // setCallbacks @@ -627,9 +622,9 @@ void BLECharacteristic::setHandle(uint16_t handle) { void BLECharacteristic::setIndicateProperty(bool value) { //ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); } } // setIndicateProperty @@ -641,9 +636,9 @@ void BLECharacteristic::setIndicateProperty(bool value) { void BLECharacteristic::setNotifyProperty(bool value) { //ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); } } // setNotifyProperty @@ -655,9 +650,9 @@ void BLECharacteristic::setNotifyProperty(bool value) { void BLECharacteristic::setReadProperty(bool value) { //ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_READ); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); } } // setReadProperty @@ -668,7 +663,7 @@ void BLECharacteristic::setReadProperty(bool value) { * @param [in] length The length of the data in bytes. */ void BLECharacteristic::setValue(uint8_t* data, size_t length) { - char *pHex = BLEUtils::buildHexData(nullptr, data, length); + char* pHex = BLEUtils::buildHexData(nullptr, data, length); ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); free(pHex); if (length > ESP_GATT_MAX_ATTR_LEN) { @@ -688,43 +683,43 @@ void BLECharacteristic::setValue(uint8_t* data, size_t length) { * @return N/A. */ void BLECharacteristic::setValue(std::string value) { - setValue((uint8_t*)(value.data()), value.length()); + setValue((uint8_t*) (value.data()), value.length()); } // setValue void BLECharacteristic::setValue(uint16_t& data16) { uint8_t temp[2]; - temp[0]=data16; - temp[1]=data16>>8; + temp[0] = data16; + temp[1] = data16 >> 8; setValue(temp, 2); } // setValue void BLECharacteristic::setValue(uint32_t& data32) { uint8_t temp[4]; - temp[0]=data32; - temp[1]=data32>>8; - temp[2]=data32>>16; - temp[3]=data32>>24; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; setValue(temp, 4); } // setValue void BLECharacteristic::setValue(int& data32) { uint8_t temp[4]; - temp[0]=data32; - temp[1]=data32>>8; - temp[2]=data32>>16; - temp[3]=data32>>24; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; setValue(temp, 4); } // setValue void BLECharacteristic::setValue(float& data32) { uint8_t temp[4]; - *((float *)temp) = data32; + *((float*) temp) = data32; setValue(temp, 4); } // setValue void BLECharacteristic::setValue(double& data64) { uint8_t temp[8]; - *((double *)temp) = data64; + *((double*) temp) = data64; setValue(temp, 8); } // setValue @@ -736,9 +731,9 @@ void BLECharacteristic::setValue(double& data64) { void BLECharacteristic::setWriteNoResponseProperty(bool value) { //ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } } // setWriteNoResponseProperty @@ -750,9 +745,9 @@ void BLECharacteristic::setWriteNoResponseProperty(bool value) { void BLECharacteristic::setWriteProperty(bool value) { //ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); + m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); } else { - m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); + m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); } } // setWriteProperty @@ -766,12 +761,12 @@ std::string BLECharacteristic::toString() { stringstream << std::hex << std::setfill('0'); stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle; stringstream << " " << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_READ)?"Read ":"") << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE)?"Write ":"") << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)?"WriteNoResponse ":"") << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST)?"Broadcast ":"") << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)?"Notify ":"") << - ((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE)?"Indicate ":""); + ((m_properties & ESP_GATT_CHAR_PROP_BIT_READ) ? "Read " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) ? "Write " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) ? "WriteNoResponse " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "Broadcast " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) ? "Notify " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) ? "Indicate " : ""); return stringstream.str(); } // toString @@ -783,7 +778,7 @@ BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} * @brief Callback function to support a read request. * @param [in] pCharacteristic The characteristic that is the source of the event. */ -void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { +void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) { ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); ESP_LOGD("BLECharacteristicCallbacks", "<< onRead"); } // onRead @@ -793,7 +788,7 @@ void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { * @brief Callback function to support a write request. * @param [in] pCharacteristic The characteristic that is the source of the event. */ -void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) { +void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) { ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite"); } // onWrite diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index e627628e..bad5ba7a 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -27,30 +27,30 @@ class BLECharacteristicCallbacks; */ class BLEDescriptorMap { public: - void setByUUID(const char* uuid, BLEDescriptor *pDescriptor); - void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); - void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); + void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); + void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); + void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); BLEDescriptor* getByUUID(const char* uuid); BLEDescriptor* getByUUID(BLEUUID uuid); BLEDescriptor* getByHandle(uint16_t handle); - std::string toString(); + std::string toString(); void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); BLEDescriptor* getFirst(); BLEDescriptor* getNext(); private: - std::map m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; }; /** * @brief The model of a %BLE Characteristic. * - * A %BLE Characteristic is an identified value container that manages a value. It is exposed by a %BLE server and + * A %BLE Characteristic is an identified value container that manages a value. It is exposed by a %BLE server and * can be read and written to by a %BLE client. */ class BLECharacteristic { @@ -59,13 +59,13 @@ class BLECharacteristic { BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); virtual ~BLECharacteristic(); - void addDescriptor(BLEDescriptor* pDescriptor); + void addDescriptor(BLEDescriptor* pDescriptor); BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); - //size_t getLength(); - BLEUUID getUUID(); - std::string getValue(); - uint8_t* getData(); + //size_t getLength(); + BLEUUID getUUID(); + std::string getValue(); + uint8_t* getData(); void indicate(); void notify(); @@ -80,19 +80,19 @@ class BLECharacteristic { void setValue(uint32_t& data32); void setValue(int& data32); void setValue(float& data32); - void setValue(double& data64); + void setValue(double& data64); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); std::string toString(); uint16_t getHandle(); void setAccessPermissions(esp_gatt_perm_t perm); - static const uint32_t PROPERTY_READ = 1<<0; - static const uint32_t PROPERTY_WRITE = 1<<1; - static const uint32_t PROPERTY_NOTIFY = 1<<2; - static const uint32_t PROPERTY_BROADCAST = 1<<3; - static const uint32_t PROPERTY_INDICATE = 1<<4; - static const uint32_t PROPERTY_WRITE_NR = 1<<5; + static const uint32_t PROPERTY_READ = 1 << 0; + static const uint32_t PROPERTY_WRITE = 1 << 1; + static const uint32_t PROPERTY_NOTIFY = 1 << 2; + static const uint32_t PROPERTY_BROADCAST = 1 << 3; + static const uint32_t PROPERTY_INDICATE = 1 << 4; + static const uint32_t PROPERTY_WRITE_NR = 1 << 5; private: @@ -101,24 +101,24 @@ class BLECharacteristic { friend class BLEDescriptor; friend class BLECharacteristicMap; - BLEUUID m_bleUUID; - BLEDescriptorMap m_descriptorMap; - uint16_t m_handle; - esp_gatt_char_prop_t m_properties; + BLEUUID m_bleUUID; + BLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + esp_gatt_char_prop_t m_properties; BLECharacteristicCallbacks* m_pCallbacks; - BLEService* m_pService; - BLEValue m_value; - esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + BLEService* m_pService; + BLEValue m_value; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - void executeCreate(BLEService* pService); + void executeCreate(BLEService* pService); esp_gatt_char_prop_t getProperties(); - BLEService* getService(); - void setHandle(uint16_t handle); + BLEService* getService(); + void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); }; // BLECharacteristic @@ -128,7 +128,7 @@ class BLECharacteristic { * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. * * When a server application creates a %BLE characteristic, we may wish to be informed when there is either - * a read or write request to the characteristic's value. An application can register a + * a read or write request to the characteristic's value. An application can register a * sub-classed instance of this class and will be notified when such an event happens. */ class BLECharacteristicCallbacks { diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index 86a97441..8e7d097d 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -56,9 +56,7 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { */ BLECharacteristic* BLECharacteristicMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; @@ -70,9 +68,7 @@ BLECharacteristic* BLECharacteristicMap::getFirst() { * @return The next characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; @@ -103,8 +99,8 @@ void BLECharacteristicMap::handleGATTServerEvent( * @return N/A. */ void BLECharacteristicMap::setByHandle(uint16_t handle, - BLECharacteristic *characteristic) { - m_handleMap.insert(std::pair(handle, characteristic)); + BLECharacteristic* characteristic) { + m_handleMap.insert(std::pair(handle, characteristic)); } // setByHandle @@ -115,9 +111,9 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, * @return N/A. */ void BLECharacteristicMap::setByUUID( - BLECharacteristic *pCharacteristic, BLEUUID uuid) { - m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); + BLECharacteristic* pCharacteristic, + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); } // setByUUID @@ -128,7 +124,7 @@ void BLECharacteristicMap::setByUUID( std::string BLECharacteristicMap::toString() { std::stringstream stringStream; stringStream << std::hex << std::setfill('0'); - int count=0; + int count = 0; for (auto &myPair: m_uuidMap) { if (count > 0) { stringStream << "\n"; diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 141cf0f5..2a8d6f0c 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -40,7 +40,6 @@ * We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics * and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors. * - * */ static const char* LOG_TAG = "BLEClient"; @@ -151,7 +150,7 @@ void BLEClient::gattClientEventHandler( esp_ble_gattc_cb_param_t* evtParam) { // Execute handler code based on the type of event received. - switch(event) { + switch (event) { // // ESP_GATTC_DISCONNECT_EVT @@ -238,7 +237,7 @@ void BLEClient::gattClientEventHandler( evtParam->search_res.start_handle, evtParam->search_res.end_handle ); - m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); + m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); break; } // ESP_GATTC_SEARCH_RES_EVT @@ -369,7 +368,7 @@ std::map* BLEClient::getServices() { ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return &m_servicesMap; } - // If sucessfull, remember that we now have services. + // If successful, remember that we now have services. m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0); ESP_LOGD(LOG_TAG, "<< getServices"); return &m_servicesMap; @@ -400,7 +399,7 @@ void BLEClient::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { ESP_LOGD(LOG_TAG, "BLEClient ... handling GAP event!"); - switch(event) { + switch (event) { // // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT // @@ -410,7 +409,7 @@ void BLEClient::handleGAPEvent( // - esp_bd_addr_t remote_addr // case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { - m_semaphoreRssiCmplEvt.give((uint32_t)param->read_rssi_cmpl.rssi); + m_semaphoreRssiCmplEvt.give((uint32_t) param->read_rssi_cmpl.rssi); break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index a60ed102..d66a8b4f 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -47,8 +47,8 @@ class BLEClient { bool isConnected(); // Return true if we are connected. - void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. + void setClientCallbacks(BLEClientCallbacks* pClientCallbacks); std::string toString(); // Return a string representation of this client. @@ -66,9 +66,9 @@ class BLEClient { uint16_t getConnId(); esp_gatt_if_t getGattcIf(); - BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. uint16_t m_conn_id; // int m_deviceType; + BLEAddress m_peerAddress = BLEAddress((uint8_t*) "\0\0\0\0\0\0"); // The BD address of the remote server. esp_gatt_if_t m_gattc_if; bool m_haveServices; // Have we previously obtain the set of services from the remote server. bool m_isConnected; // Are we currently connected. @@ -80,7 +80,6 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; void clearServices(); // Clear any existing services. - }; // class BLEDevice diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 58ff78b4..5f23026f 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -37,13 +37,13 @@ BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) { */ BLEDescriptor::BLEDescriptor(BLEUUID uuid) { m_bleUUID = uuid; - m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. m_value.attr_len = 0; // Initial length is 0. m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; // Maximum length of the data. m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCharacteristic = nullptr; // No initial characteristic. m_pCallback = nullptr; // No initial callback. + m_value.attr_value = (uint8_t*) malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. } // BLEDescriptor @@ -133,8 +133,8 @@ uint8_t* BLEDescriptor::getValue() { void BLEDescriptor::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - switch(event) { + esp_ble_gatts_cb_param_t* param) { + switch (event) { // ESP_GATTS_ADD_CHAR_DESCR_EVT // // add_char_descr: @@ -247,10 +247,9 @@ void BLEDescriptor::handleGATTServerEvent( break; } // ESP_GATTS_READ_EVT - default: { + default: break; - } - }// switch event + } // switch event } // handleGATTServerEvent @@ -259,7 +258,7 @@ void BLEDescriptor::handleGATTServerEvent( * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. */ void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) { - ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallback); + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback); m_pCallback = pCallback; ESP_LOGD(LOG_TAG, "<< setCallbacks"); } // setCallbacks @@ -298,7 +297,7 @@ void BLEDescriptor::setValue(uint8_t* data, size_t length) { * @param [in] value The value of the descriptor in string form. */ void BLEDescriptor::setValue(std::string value) { - setValue((uint8_t *)value.data(), value.length()); + setValue((uint8_t*) value.data(), value.length()); } // setValue void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) { diff --git a/cpp_utils/BLEDescriptorMap.cpp b/cpp_utils/BLEDescriptorMap.cpp index 4e372e1d..075b3025 100644 --- a/cpp_utils/BLEDescriptorMap.cpp +++ b/cpp_utils/BLEDescriptorMap.cpp @@ -21,7 +21,7 @@ * @return The descriptor. If not present, then nullptr is returned. */ BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } @@ -57,8 +57,8 @@ BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) { * @param [in] characteristic The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor *pDescriptor){ - m_uuidMap.insert(std::pair(uuid, pDescriptor)); +void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ + m_uuidMap.insert(std::pair(uuid, pDescriptor)); } // setByUUID @@ -69,8 +69,8 @@ void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor *pDescriptor){ * @param [in] characteristic The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor) { - m_uuidMap.insert(std::pair(uuid.toString(), pDescriptor)); +void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) { + m_uuidMap.insert(std::pair(uuid.toString(), pDescriptor)); } // setByUUID @@ -80,9 +80,8 @@ void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor) { * @param [in] descriptor The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByHandle(uint16_t handle, - BLEDescriptor *pDescriptor) { - m_handleMap.insert(std::pair(handle, pDescriptor)); +void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) { + m_handleMap.insert(std::pair(handle, pDescriptor)); } // setByHandle @@ -93,8 +92,8 @@ void BLEDescriptorMap::setByHandle(uint16_t handle, std::string BLEDescriptorMap::toString() { std::stringstream stringStream; stringStream << std::hex << std::setfill('0'); - int count=0; - for (auto &myPair: m_uuidMap) { + int count = 0; + for (auto &myPair : m_uuidMap) { if (count > 0) { stringStream << "\n"; } @@ -114,7 +113,7 @@ std::string BLEDescriptorMap::toString() { void BLEDescriptorMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { + esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every descriptor we have. for (auto &myPair : m_uuidMap) { myPair.second->handleGATTServerEvent(event, gatts_if, param); @@ -128,10 +127,8 @@ void BLEDescriptorMap::handleGATTServerEvent( */ BLEDescriptor* BLEDescriptorMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEDescriptor *pRet = m_iterator->second; + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->second; m_iterator++; return pRet; } // getFirst @@ -142,10 +139,8 @@ BLEDescriptor* BLEDescriptorMap::getFirst() { * @return The next descriptor in the map. */ BLEDescriptor* BLEDescriptorMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEDescriptor *pRet = m_iterator->second; + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->second; m_iterator++; return pRet; } // getNext diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 157074d4..fd86abe0 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -42,7 +42,7 @@ BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; bool initialized = false; // Have we been initialized? -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t) 0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; uint16_t BLEDevice::m_localMTU = 23; BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; @@ -98,11 +98,11 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; BLEUtils::dumpGattServerEvent(event, gatts_if, param); - switch(event) { + switch (event) { case ESP_GATTS_CONNECT_EVT: { BLEDevice::m_localMTU = 23; #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ + if (BLEDevice::m_securityLevel) { esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } #endif // CONFIG_BLE_SMP_ENABLE @@ -146,23 +146,22 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; switch(event) { case ESP_GATTC_CONNECT_EVT: { - if(BLEDevice::getMTU() != 23){ + if (BLEDevice::getMTU() != 23) { esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityLevel){ + if (BLEDevice::m_securityLevel) { esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } #endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTC_CONNECT_EVT - default: { + default: break; - } } // switch @@ -179,34 +178,33 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; */ /* STATIC */ void BLEDevice::gapEventHandler( esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param) { + esp_ble_gap_cb_param_t* param) { BLEUtils::dumpGapEvent(event, param); - switch(event) { - - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); - break; - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); - break; - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); - break; - case ESP_GAP_BLE_NC_REQ_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + switch (event) { + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if (BLEDevice::m_securityCallbacks != nullptr) { + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); } #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); } #endif // CONFIG_BLE_SMP_ENABLE @@ -214,15 +212,14 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; /* * TODO should we add white/black list comparison? */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); - } - else{ + } else { esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); } #endif // CONFIG_BLE_SMP_ENABLE @@ -230,34 +227,33 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; /* * */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - ///show the passkey number to the user to input it in the peer deivce. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + // show the passkey number to the user to input it in the peer device. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if (BLEDevice::m_securityCallbacks != nullptr) { ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); } #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key type info share with peer device to the user. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if(BLEDevice::m_securityCallbacks!=nullptr){ - BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - } + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if (BLEDevice::m_securityCallbacks != nullptr) { + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } #endif // CONFIG_BLE_SMP_ENABLE - break; - default: { + break; + default: break; - } } // switch if (BLEDevice::m_pServer != nullptr) { @@ -316,7 +312,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; */ /* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); + BLEClient* pClient = createClient(); pClient->connect(bdAddress); std::string ret = pClient->getValue(serviceUUID, characteristicUUID); pClient->disconnect(); @@ -330,8 +326,8 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; * @param deviceName The device name of the device. */ /* STATIC */ void BLEDevice::init(std::string deviceName) { - if(!initialized){ - initialized = true; // Set the initialization flag to ensure we are only initialized once. + if (!initialized) { + initialized = true; // Set the initialization flag to ensure we are only initialized once. esp_err_t errRc = ESP_OK; #ifdef ARDUINO_ARCH_ESP32 @@ -354,13 +350,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; } #ifndef CLASSIC_BT_ENABLED - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue - errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); #else errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (errRc != ESP_OK) { @@ -371,7 +361,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; #endif esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); - if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ + if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) { errRc = esp_bluedroid_init(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -379,7 +369,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; } } - if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ + if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) { errRc = esp_bluedroid_enable(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -424,7 +414,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; }; #endif // CONFIG_BLE_SMP_ENABLE } - vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. + vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. } // init @@ -459,7 +449,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; */ /* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); - BLEClient *pClient = createClient(); + BLEClient* pClient = createClient(); pClient->connect(bdAddress); pClient->setValue(serviceUUID, characteristicUUID, value); pClient->disconnect(); @@ -527,7 +517,7 @@ void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { esp_err_t BLEDevice::setMTU(uint16_t mtu) { ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); - if(err == ESP_OK){ + if (err == ESP_OK) { m_localMTU = mtu; } else { ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); @@ -548,8 +538,7 @@ bool BLEDevice::getInitialized() { } BLEAdvertising* BLEDevice::getAdvertising() { - if(m_bleAdvertising == nullptr) - { + if (m_bleAdvertising == nullptr) { m_bleAdvertising = new BLEAdvertising(); ESP_LOGI(LOG_TAG, "create advertising"); } diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 63183d0b..9fd53ebb 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -47,13 +47,13 @@ class BLEDevice { static void startAdvertising(); private: - static BLEServer *m_pServer; - static BLEScan *m_pScan; - static BLEClient *m_pClient; - static esp_ble_sec_act_t m_securityLevel; + static BLEServer* m_pServer; + static BLEScan* m_pScan; + static BLEClient* m_pClient; + static esp_ble_sec_act_t m_securityLevel; static BLESecurityCallbacks* m_securityCallbacks; - static uint16_t m_localMTU; - static BLEAdvertising *m_bleAdvertising; + static uint16_t m_localMTU; + static BLEAdvertising* m_bleAdvertising; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLEEddystoneTLM.cpp b/cpp_utils/BLEEddystoneTLM.cpp index b5466b13..a92bcdb2 100755 --- a/cpp_utils/BLEEddystoneTLM.cpp +++ b/cpp_utils/BLEEddystoneTLM.cpp @@ -16,21 +16,21 @@ static const char LOG_TAG[] = "BLEEddystoneTLM"; #define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) BLEEddystoneTLM::BLEEddystoneTLM() { - beconUUID = 0xFEAA; - m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; - m_eddystoneData.version = 0; - m_eddystoneData.volt = 3300; // 3300mV = 3.3V - m_eddystoneData.temp = (uint16_t)((float)23.00); - m_eddystoneData.advCount = 0; - m_eddystoneData.tmil = 0; + beaconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t) ((float) 23.00); + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; } // BLEEddystoneTLM std::string BLEEddystoneTLM::getData() { - return std::string((char*)&m_eddystoneData, sizeof(m_eddystoneData)); + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); } // getData BLEUUID BLEEddystoneTLM::getUUID() { - return BLEUUID(beconUUID); + return BLEUUID(beaconUUID); } // getUUID uint8_t BLEEddystoneTLM::getVersion() { @@ -60,35 +60,35 @@ std::string BLEEddystoneTLM::toString() { ss << "Version "; ss << std::dec << m_eddystoneData.version; ss << "\n"; - + ss << "Battery Voltage "; ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt); ss << " mV\n"; - - ss << "Temperature "; - ss << (float)m_eddystoneData.temp; - ss << " °C\n"; - - ss << "Adv. Count "; + + ss << "Temperature "; + ss << (float) m_eddystoneData.temp; + ss << " °C\n"; + + ss << "Adv. Count "; ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount); ss << "\n"; - + ss << "Time "; rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); std::stringstream buffstream; buffstream << "0000"; - buffstream << std::dec << rawsec/864000; + buffstream << std::dec << rawsec / 864000; std::string buff = buffstream.str(); - ss << buff.substr(buff.length()-4, buff.length()); + ss << buff.substr(buff.length() - 4, buff.length()); ss << "."; buffstream.str(""); buffstream.clear(); buffstream << "00"; - buffstream << std::dec << (rawsec/36000)%24; + buffstream << std::dec << (rawsec / 36000) % 24; buff = buffstream.str(); ss << buff.substr(buff.length()-2, buff.length()); ss << ":"; @@ -96,17 +96,17 @@ std::string BLEEddystoneTLM::toString() { buffstream.str(""); buffstream.clear(); buffstream << "00"; - buffstream << std::dec << (rawsec/600)%60; + buffstream << std::dec << (rawsec / 600) % 60; buff = buffstream.str(); - ss << buff.substr(buff.length()-2, buff.length()); + ss << buff.substr(buff.length() - 2, buff.length()); ss << ":"; buffstream.str(""); buffstream.clear(); buffstream << "00"; - buffstream << std::dec << (rawsec/10)%60; + buffstream << std::dec << (rawsec / 10) % 60; buff = buffstream.str(); - ss << buff.substr(buff.length()-2, buff.length()); + ss << buff.substr(buff.length() - 2, buff.length()); ss << "\n"; return ss.str(); @@ -124,7 +124,7 @@ void BLEEddystoneTLM::setData(std::string data) { } // setData void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) { - beconUUID = l_uuid.getNative()->uuid.uuid16; + beaconUUID = l_uuid.getNative()->uuid.uuid16; } // setUUID void BLEEddystoneTLM::setVersion(uint8_t version) { diff --git a/cpp_utils/BLEEddystoneTLM.h b/cpp_utils/BLEEddystoneTLM.h index 76bd6a43..a93e224f 100755 --- a/cpp_utils/BLEEddystoneTLM.h +++ b/cpp_utils/BLEEddystoneTLM.h @@ -17,33 +17,34 @@ * * https://github.com/google/eddystone */ class BLEEddystoneTLM { +public: + BLEEddystoneTLM(); + std::string getData(); + BLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + private: - uint16_t beconUUID; + uint16_t beaconUUID; struct { uint8_t frameType; - int8_t version; + uint8_t version; uint16_t volt; uint16_t temp; uint32_t advCount; uint32_t tmil; - } __attribute__((packed))m_eddystoneData; -public: - BLEEddystoneTLM(); - std::string getData(); - BLEUUID getUUID(); - uint8_t getVersion(); - uint16_t getVolt(); - float getTemp(); - uint32_t getCount(); - uint32_t getTime(); - std::string toString(); - void setData(std::string data); - void setUUID(BLEUUID l_uuid); - void setVersion(uint8_t version); - void setVolt(uint16_t volt); - void setTemp(float temp); - void setCount(uint32_t advCount); - void setTime(uint32_t tmil); + } __attribute__((packed)) m_eddystoneData; }; // BLEEddystoneTLM diff --git a/cpp_utils/BLEEddystoneURL.cpp b/cpp_utils/BLEEddystoneURL.cpp index 6c12b246..af3b674c 100755 --- a/cpp_utils/BLEEddystoneURL.cpp +++ b/cpp_utils/BLEEddystoneURL.cpp @@ -13,19 +13,19 @@ static const char LOG_TAG[] = "BLEEddystoneURL"; BLEEddystoneURL::BLEEddystoneURL() { - beconUUID = 0xFEAA; - lengthURL = 0; - m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; - m_eddystoneData.advertisedTxPower = 0; - memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + beaconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); } // BLEEddystoneURL std::string BLEEddystoneURL::getData() { - return std::string((char*)&m_eddystoneData, sizeof(m_eddystoneData)); + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); } // getData BLEUUID BLEEddystoneURL::getUUID() { - return BLEUUID(beconUUID); + return BLEUUID(beaconUUID); } // getUUID int8_t BLEEddystoneURL::getPower() { @@ -33,7 +33,7 @@ int8_t BLEEddystoneURL::getPower() { } // getPower std::string BLEEddystoneURL::getURL() { - return std::string((char*)&m_eddystoneData.url, sizeof(m_eddystoneData.url)); + return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); } // getURL std::string BLEEddystoneURL::getDecodedURL() { @@ -56,58 +56,58 @@ std::string BLEEddystoneURL::getDecodedURL() { decodedURL += m_eddystoneData.url[0]; } - for (int i=1;i33&&m_eddystoneData.url[i]<127) { - decodedURL += m_eddystoneData.url[i]; - } else { - switch (m_eddystoneData.url[i]) { - case 0x00: - decodedURL += ".com/"; - break; - case 0x01: - decodedURL += ".org/"; - break; - case 0x02: - decodedURL += ".edu/"; - break; - case 0x03: - decodedURL += ".net/"; - break; - case 0x04: - decodedURL += ".info/"; - break; - case 0x05: - decodedURL += ".biz/"; - break; - case 0x06: - decodedURL += ".gov/"; - break; - case 0x07: - decodedURL += ".com"; - break; - case 0x08: - decodedURL += ".org"; - break; - case 0x09: - decodedURL += ".edu"; - break; - case 0x0A: - decodedURL += ".net"; - break; - case 0x0B: - decodedURL += ".info"; - break; - case 0x0C: - decodedURL += ".biz"; - break; - case 0x0D: - decodedURL += ".gov"; - break; - } - } - } - - + for (int i = 1; i < lengthURL; i++) { + if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + default: + break; + } + } + } return decodedURL; } // getDecodedURL @@ -121,14 +121,13 @@ void BLEEddystoneURL::setData(std::string data) { ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData)); return; } - memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); memcpy(&m_eddystoneData, data.data(), data.length()); - lengthURL=data.length()-(sizeof(m_eddystoneData)-sizeof(m_eddystoneData.url)); - + lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); } // setData void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { - beconUUID = l_uuid.getNative()->uuid.uuid16; + beaconUUID = l_uuid.getNative()->uuid.uuid16; } // setUUID void BLEEddystoneURL::setPower(int8_t advertisedTxPower) { @@ -137,12 +136,12 @@ void BLEEddystoneURL::setPower(int8_t advertisedTxPower) { void BLEEddystoneURL::setURL(std::string url) { if (url.length() > sizeof(m_eddystoneData.url)) { - ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url)); - return; + ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url)); + return; } memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); memcpy(m_eddystoneData.url, url.data(), url.length()); - lengthURL=url.length(); + lengthURL = url.length(); } // setURL diff --git a/cpp_utils/BLEEddystoneURL.h b/cpp_utils/BLEEddystoneURL.h index 2025cb19..0b538c07 100755 --- a/cpp_utils/BLEEddystoneURL.h +++ b/cpp_utils/BLEEddystoneURL.h @@ -17,25 +17,26 @@ * * https://github.com/google/eddystone */ class BLEEddystoneURL { -private: - uint16_t beconUUID; - uint8_t lengthURL; - struct { - uint8_t frameType; - int8_t advertisedTxPower; - uint8_t url[16]; - } __attribute__((packed))m_eddystoneData; public: BLEEddystoneURL(); std::string getData(); - BLEUUID getUUID(); - int8_t getPower(); + BLEUUID getUUID(); + int8_t getPower(); std::string getURL(); std::string getDecodedURL(); - void setData(std::string data); - void setUUID(BLEUUID l_uuid); - void setPower(int8_t advertisedTxPower); - void setURL(std::string url); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(std::string url); + +private: + uint16_t beaconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed)) m_eddystoneData; }; // BLEEddystoneURL diff --git a/cpp_utils/BLEExceptions.h b/cpp_utils/BLEExceptions.h index 369fcaf6..ea9db855 100644 --- a/cpp_utils/BLEExceptions.h +++ b/cpp_utils/BLEExceptions.h @@ -17,13 +17,13 @@ class BLEDisconnectedException : public std::exception { - const char *what() const throw () { + const char* what() const throw () { return "BLE Disconnected"; } }; class BLEUuidNotFoundException : public std::exception { - const char *what() const throw () { + const char* what() const throw () { return "No such UUID"; } }; diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index cc6c531d..69e18be7 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -22,15 +22,15 @@ BLEHIDDevice::BLEHIDDevice(BLEServer* server) { /* * Mandatory characteristic for device info service */ - m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, BLECharacteristic::PROPERTY_READ); + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ); /* * Mandatory characteristics for HID service */ - m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, BLECharacteristic::PROPERTY_READ); - m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, BLECharacteristic::PROPERTY_READ); - m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); - m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); /* * Mandatory battery level characteristic with notification and presence descriptor @@ -40,7 +40,7 @@ BLEHIDDevice::BLEHIDDevice(BLEServer* server) { batteryLevelDescriptor->setNamespace(1); batteryLevelDescriptor->setUnit(0x27ad); - m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); m_batteryLevelCharacteristic->addDescriptor(new BLE2902()); @@ -48,8 +48,8 @@ BLEHIDDevice::BLEHIDDevice(BLEServer* server) { * This value is setup here because its default value in most usage cases, its very rare to use boot mode * and we want to simplify library using as much as possible */ - const uint8_t pMode[] = {0x01}; - protocolMode()->setValue((uint8_t*)pMode, 1); + const uint8_t pMode[] = { 0x01 }; + protocolMode()->setValue((uint8_t*) pMode, 1); } BLEHIDDevice::~BLEHIDDevice() { @@ -74,8 +74,8 @@ void BLEHIDDevice::startServices() { /* * @brief Create manufacturer characteristic (this characteristic is optional) */ -BLECharacteristic* BLEHIDDevice::manufacturer() { - m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, BLECharacteristic::PROPERTY_READ); +BLECharacteristic* BLEHIDDevice::manufacturer() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ); return m_manufacturerCharacteristic; } @@ -91,7 +91,7 @@ void BLEHIDDevice::manufacturer(std::string name) { * @brief */ void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { - uint8_t pnp[] = {sig, (uint8_t)(vid>>8), (uint8_t)vid, (uint8_t)(pid>>8), (uint8_t)pid, (uint8_t)(version>>8), (uint8_t)version}; + uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); } @@ -99,8 +99,8 @@ void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version * @brief */ void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { - uint8_t info[] = {0x11,0x1, country, flags}; - m_hidInfoCharacteristic->setValue(info, sizeof(info));; + uint8_t info[] = { 0x11, 0x1, country, flags }; + m_hidInfoCharacteristic->setValue(info, sizeof(info)); } /* @@ -109,15 +109,15 @@ void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { * @return pointer to new input report characteristic */ BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { - BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); - BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); BLE2902* p2902 = new BLE2902(); inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - uint8_t desc1_val[] = {reportID, 0x01}; - inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); + uint8_t desc1_val[] = { reportID, 0x01 }; + inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); inputReportCharacteristic->addDescriptor(p2902); inputReportCharacteristic->addDescriptor(inputReportDescriptor); @@ -130,13 +130,13 @@ BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { * @return Pointer to new output report characteristic */ BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { - BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); - BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - uint8_t desc1_val[] = {reportID, 0x02}; - outputReportDescriptor->setValue((uint8_t*)desc1_val, 2); + uint8_t desc1_val[] = { reportID, 0x02 }; + outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); outputReportCharacteristic->addDescriptor(outputReportDescriptor); return outputReportCharacteristic; @@ -148,13 +148,14 @@ BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { * @return Pointer to new feature report characteristic */ BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { - BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); - BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); + BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); + BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - uint8_t desc1_val[] = {reportID, 0x03}; - featureReportDescriptor->setValue((uint8_t*)desc1_val, 2); + uint8_t desc1_val[] = { reportID, 0x03 }; + featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); featureReportCharacteristic->addDescriptor(featureReportDescriptor); return featureReportCharacteristic; @@ -164,7 +165,7 @@ BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { * @brief */ BLECharacteristic* BLEHIDDevice::bootInput() { - BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY); + BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY); bootInputCharacteristic->addDescriptor(new BLE2902()); return bootInputCharacteristic; @@ -174,20 +175,20 @@ BLECharacteristic* BLEHIDDevice::bootInput() { * @brief */ BLECharacteristic* BLEHIDDevice::bootOutput() { - return m_hidService->createCharacteristic((uint16_t)0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); } /* * @brief */ -BLECharacteristic* BLEHIDDevice::hidControl() { +BLECharacteristic* BLEHIDDevice::hidControl() { return m_hidControlCharacteristic; } /* * @brief */ -BLECharacteristic* BLEHIDDevice::protocolMode() { +BLECharacteristic* BLEHIDDevice::protocolMode() { return m_protocolModeCharacteristic; } @@ -204,11 +205,11 @@ BLECharacteristic* BLEHIDDevice::batteryLevel() { -BLECharacteristic* BLEHIDDevice::reportMap() { +BLECharacteristic* BLEHIDDevice::reportMap() { return m_reportMapCharacteristic; } -BLECharacteristic* BLEHIDDevice::pnp() { +BLECharacteristic* BLEHIDDevice::pnp() { return m_pnpCharacteristic; } diff --git a/cpp_utils/BLEHIDDevice.h b/cpp_utils/BLEHIDDevice.h index 319fd42a..33e6b46c 100644 --- a/cpp_utils/BLEHIDDevice.h +++ b/cpp_utils/BLEHIDDevice.h @@ -17,15 +17,15 @@ #include "BLE2902.h" #include "HIDTypes.h" -#define GENERIC_HID 960 -#define HID_KEYBOARD 961 -#define HID_MOUSE 962 -#define HID_JOYSTICK 963 -#define HID_GAMEPAD 964 -#define HID_TABLET 965 -#define HID_CARD_READER 966 -#define HID_DIGITAL_PEN 967 -#define HID_BARCODE 968 +#define GENERIC_HID 0x03C0 +#define HID_KEYBOARD 0x03C1 +#define HID_MOUSE 0x03C2 +#define HID_JOYSTICK 0x03C3 +#define HID_GAMEPAD 0x03C4 +#define HID_TABLET 0x03C5 +#define HID_CARD_READER 0x03C6 +#define HID_DIGITAL_PEN 0x03C7 +#define HID_BARCODE 0x03C8 class BLEHIDDevice { public: diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 33d43eee..14177eac 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -146,12 +146,8 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { * @param [in] evtParam Payload data for the event. * @returns N/A */ -void BLERemoteCharacteristic::gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* evtParam) { - switch(event) { - // +void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { + switch (event) { // ESP_GATTC_NOTIFY_EVT // // notify @@ -165,25 +161,15 @@ void BLERemoteCharacteristic::gattClientEventHandler( // We have received a notification event which means that the server wishes us to know about a notification // piece of data. What we must now do is find the characteristic with the associated handle and then // invoke its notification callback (if it has one). - // case ESP_GATTC_NOTIFY_EVT: { - if (evtParam->notify.handle != getHandle()) { - break; - } + if (evtParam->notify.handle != getHandle()) break; if (m_notifyCallback != nullptr) { ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str()); - m_notifyCallback( - this, - evtParam->notify.value, - evtParam->notify.value_len, - evtParam->notify.is_notify - ); + m_notifyCallback(this, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify); } // End we have a callback function ... break; } // ESP_GATTC_NOTIFY_EVT - - // // ESP_GATTC_READ_CHAR_EVT // This event indicates that the server has responded to the read request. // @@ -193,20 +179,16 @@ void BLERemoteCharacteristic::gattClientEventHandler( // - uint16_t handle // - uint8_t* value // - uint16_t value_len - // case ESP_GATTC_READ_CHAR_EVT: { // If this event is not for us, then nothing further to do. - if (evtParam->read.handle != getHandle()) { - break; - } + if (evtParam->read.handle != getHandle()) break; // At this point, we have determined that the event is for us, so now we save the value // and unlock the semaphore to ensure that the requestor of the data can continue. if (evtParam->read.status == ESP_GATT_OK) { - m_value = std::string((char*)evtParam->read.value, evtParam->read.value_len); - if(m_rawData != nullptr) - free(m_rawData); - + m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len); + if (m_rawData != nullptr) free(m_rawData); + m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); } else { @@ -217,56 +199,41 @@ void BLERemoteCharacteristic::gattClientEventHandler( break; } // ESP_GATTC_READ_CHAR_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: { // If the request is not for this BLERemoteCharacteristic then move on to the next. - if (evtParam->reg_for_notify.handle != getHandle()) { - break; - } + if (evtParam->reg_for_notify.handle != getHandle()) break; // We have processed the notify registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT - - // // ESP_GATTC_UNREG_FOR_NOTIFY_EVT // // unreg_for_notify: // - esp_gatt_status_t status // - uint16_t handle - // case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { - if (evtParam->unreg_for_notify.handle != getHandle()) { - break; - } + if (evtParam->unreg_for_notify.handle != getHandle()) break; // We have processed the notify un-registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT: - - // // ESP_GATTC_WRITE_CHAR_EVT // // write: // - esp_gatt_status_t status // - uint16_t conn_id // - uint16_t handle - // case ESP_GATTC_WRITE_CHAR_EVT: { // Determine if this event is for us and, if not, pass onwards. - if (evtParam->write.handle != getHandle()) { - break; - } + if (evtParam->write.handle != getHandle()) break; // There is nothing further we need to do here. This is merely an indication // that the write has completed and we can unlock the caller. @@ -275,9 +242,8 @@ void BLERemoteCharacteristic::gattClientEventHandler( } // ESP_GATTC_WRITE_CHAR_EVT - default: { + default: break; - } } // End switch }; // gattClientEventHandler @@ -294,7 +260,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { // For each descriptor we find, create a BLERemoteDescriptor instance. uint16_t offset = 0; esp_gattc_descr_elem_t result; - while(1) { + while (true) { uint16_t count = 1; esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( getRemoteService()->getClient()->getGattcIf(), @@ -314,14 +280,12 @@ void BLERemoteCharacteristic::retrieveDescriptors() { break; } - if (count == 0) { - break; - } + if (count == 0) break; ESP_LOGE(LOG_TAG, ""); ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); // We now have a new characteristic ... let us add that to our set of known characteristics - BLERemoteDescriptor *pNewRemoteDescriptor = new BLERemoteDescriptor( + BLERemoteDescriptor* pNewRemoteDescriptor = new BLERemoteDescriptor( result.handle, BLEUUID(result.uuid), this @@ -339,7 +303,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { /** * @brief Retrieve the map of descriptors keyed by UUID. */ -std::map* BLERemoteCharacteristic::getDescriptors() { +std::map* BLERemoteCharacteristic::getDescriptors() { return &m_descriptorMap; } // getDescriptors @@ -363,7 +327,7 @@ uint16_t BLERemoteCharacteristic::getHandle() { BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); std::string v = uuid.toString(); - for (auto &myPair : m_descriptorMap) { + for (auto& myPair : m_descriptorMap) { if (myPair.first == v) { ESP_LOGD(LOG_TAG, "<< getDescriptor: found"); return myPair.second; @@ -396,10 +360,10 @@ BLEUUID BLERemoteCharacteristic::getUUID() { * @brief Read an unsigned 16 bit value * @return The unsigned 16 bit value. */ -uint16_t BLERemoteCharacteristic::readUInt16(void) { +uint16_t BLERemoteCharacteristic::readUInt16() { std::string value = readValue(); if (value.length() >= 2) { - return *(uint16_t*)(value.data()); + return *(uint16_t*) value.data(); } return 0; } // readUInt16 @@ -409,10 +373,10 @@ uint16_t BLERemoteCharacteristic::readUInt16(void) { * @brief Read an unsigned 32 bit value. * @return the unsigned 32 bit value. */ -uint32_t BLERemoteCharacteristic::readUInt32(void) { +uint32_t BLERemoteCharacteristic::readUInt32() { std::string value = readValue(); if (value.length() >= 4) { - return *(uint32_t*)(value.data()); + return *(uint32_t*) value.data(); } return 0; } // readUInt32 @@ -422,10 +386,10 @@ uint32_t BLERemoteCharacteristic::readUInt32(void) { * @brief Read a byte value * @return The value as a byte */ -uint8_t BLERemoteCharacteristic::readUInt8(void) { +uint8_t BLERemoteCharacteristic::readUInt8() { std::string value = readValue(); if (value.length() >= 1) { - return (uint8_t)value[0]; + return (uint8_t) value[0]; } return 0; } // readUInt8 @@ -499,9 +463,8 @@ void BLERemoteCharacteristic::registerForNotify( } uint8_t val[] = {0x01, 0x00}; - if(!notifications) - val[0] = 0x02; - BLERemoteDescriptor *desc = getDescriptor(BLEUUID("0x2902")); + if (!notifications) val[0] = 0x02; + BLERemoteDescriptor* desc = getDescriptor(BLEUUID("0x2902")); desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. @@ -516,7 +479,7 @@ void BLERemoteCharacteristic::registerForNotify( } uint8_t val[] = {0x00, 0x00}; - BLERemoteDescriptor *desc = getDescriptor(BLEUUID("0x2902")); + BLERemoteDescriptor* desc = getDescriptor(BLEUUID("0x2902")); desc->writeValue(val, 2); } // End Unregister @@ -615,11 +578,11 @@ void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { * @param [in] response Whether we require a response from the write. */ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { - writeValue(std::string((char *)data, length), response); + writeValue(std::string((char*) data, length), response); } // writeValue /** - * @brief Read raw data from remote characteristic as hex bytes + * @brief Read raw data from remote characteristic as hex bytes * @return return pointer data read */ uint8_t* BLERemoteCharacteristic::readRawData() { diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 044f8f62..0750c1a9 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -37,9 +37,9 @@ class BLERemoteCharacteristic { bool canWrite(); bool canWriteNoResponse(); BLERemoteDescriptor* getDescriptor(BLEUUID uuid); - std::map* getDescriptors(); uint16_t getHandle(); BLEUUID getUUID(); + std::map* getDescriptors(); std::string readValue(void); uint8_t readUInt8(void); uint16_t readUInt16(void); @@ -59,25 +59,24 @@ class BLERemoteCharacteristic { // Private member functions void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - BLERemoteService* getRemoteService(); - void removeDescriptors(); - void retrieveDescriptors(); + void removeDescriptors(); + void retrieveDescriptors(); // Private properties - BLEUUID m_uuid; + BLEUUID m_uuid; esp_gatt_char_prop_t m_charProp; - uint16_t m_handle; - BLERemoteService* m_pRemoteService; - FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); + uint16_t m_handle; + BLERemoteService* m_pRemoteService; + FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); - FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); - std::string m_value; - uint8_t *m_rawData; + FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); + std::string m_value; + uint8_t* m_rawData; void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 2cdc7db1..4947976b 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -55,7 +55,7 @@ BLEUUID BLERemoteDescriptor::getUUID() { } // getUUID -std::string BLERemoteDescriptor::readValue(void) { +std::string BLERemoteDescriptor::readValue() { ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); // Check to see that we are connected. @@ -87,28 +87,28 @@ std::string BLERemoteDescriptor::readValue(void) { } // readValue -uint8_t BLERemoteDescriptor::readUInt8(void) { +uint8_t BLERemoteDescriptor::readUInt8() { std::string value = readValue(); if (value.length() >= 1) { - return (uint8_t)value[0]; + return (uint8_t) value[0]; } return 0; } // readUInt8 -uint16_t BLERemoteDescriptor::readUInt16(void) { +uint16_t BLERemoteDescriptor::readUInt16() { std::string value = readValue(); if (value.length() >= 2) { - return *(uint16_t*)(value.data()); + return *(uint16_t*) value.data(); } return 0; } // readUInt16 -uint32_t BLERemoteDescriptor::readUInt32(void) { +uint32_t BLERemoteDescriptor::readUInt32() { std::string value = readValue(); if (value.length() >= 4) { - return *(uint32_t*)(value.data()); + return *(uint32_t*) value.data(); } return 0; } // readUInt32 @@ -118,7 +118,7 @@ uint32_t BLERemoteDescriptor::readUInt32(void) { * @brief Return a string representation of this BLE Remote Descriptor. * @retun A string representation of this BLE Remote Descriptor. */ -std::string BLERemoteDescriptor::toString(void) { +std::string BLERemoteDescriptor::toString() { std::stringstream ss; ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); return ss.str(); @@ -131,10 +131,7 @@ std::string BLERemoteDescriptor::toString(void) { * @param [in] length The length of the data to send. * @param [in] response True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - uint8_t* data, - size_t length, - bool response) { +void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) { ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); // Check to see that we are connected. if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { @@ -163,10 +160,8 @@ void BLERemoteDescriptor::writeValue( * @param [in] newValue The data to send to the remote descriptor. * @param [in] response True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - std::string newValue, - bool response) { - writeValue(newValue.data(), newValue.length()); +void BLERemoteDescriptor::writeValue(std::string newValue, bool response) { + writeValue(newValue.data(), newValue.length(), response); } // writeValue @@ -175,9 +170,7 @@ void BLERemoteDescriptor::writeValue( * @param [in] The single byte to write. * @param [in] True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - uint8_t newValue, - bool response) { +void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { writeValue(&newValue, 1, response); } // writeValue diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index ef0588bc..a1bf06e7 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -60,8 +60,8 @@ static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { void BLERemoteService::gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *evtParam) { - switch(event) { + esp_ble_gattc_cb_param_t* evtParam) { + switch (event) { // // ESP_GATTC_GET_CHAR_EVT // @@ -108,9 +108,8 @@ void BLERemoteService::gattClientEventHandler( break; } // ESP_GATTC_GET_CHAR_EVT */ - default: { + default: break; - } } // switch // Send the event to each of the characteristics owned by this service. @@ -127,10 +126,10 @@ void BLERemoteService::gattClientEventHandler( * @throws BLEUuidNotFoundException */ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { - return getCharacteristic(BLEUUID(uuid)); + return getCharacteristic(BLEUUID(uuid)); } // getCharacteristic - - + + /** * @brief Get the characteristic object for the UUID. * @param [in] uuid Characteristic uuid. @@ -163,14 +162,13 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { * @return N/A */ void BLERemoteService::retrieveCharacteristics() { - ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); removeCharacteristics(); // Forget any previous characteristics. uint16_t offset = 0; esp_gattc_char_elem_t result; - while(1) { + while (true) { uint16_t count = 1; esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( getClient()->getGattcIf(), @@ -219,7 +217,7 @@ void BLERemoteService::retrieveCharacteristics() { * @brief Retrieve a map of all the characteristics of this service. * @return A map of all the characteristics of this service. */ -std::map * BLERemoteService::getCharacteristics() { +std::map* BLERemoteService::getCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); // If is possible that we have not read the characteristics associated with the service so do that // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking @@ -234,7 +232,7 @@ std::map * BLERemoteService::getCharacte /** * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID */ -void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap){ +void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { pCharacteristicMap = &m_characteristicMapByHandle; } // Get the characteristics map. diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 222c6e45..387afbdf 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -26,7 +26,6 @@ class BLERemoteCharacteristic; */ class BLERemoteService { public: - virtual ~BLERemoteService(); // Public methods @@ -67,10 +66,10 @@ class BLERemoteService { // Properties // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. - std::map m_characteristicMap; + std::map m_characteristicMap; // We maintain a map of characteristics owned by this service keyed by a handle. - std::map m_characteristicMapByHandle; + std::map m_characteristicMapByHandle; bool m_haveCharacteristics; // Have we previously obtained the characteristics. BLEClient* m_pClient; diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 3046b7c8..25b60a0b 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -48,7 +48,7 @@ void BLEScan::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { - switch(event) { + switch (event) { // ESP_GAP_BLE_SCAN_RESULT_EVT // --------------------------- @@ -90,12 +90,12 @@ void BLEScan::handleGAPEvent( break; } -// Examine our list of previously scanned addresses and, if we found this one already, -// ignore it. + // Examine our list of previously scanned addresses and, if we found this one already, + // ignore it. BLEAddress advertisedAddress(param->scan_rst.bda); bool found = false; - for (int i=0; i> Dump scan results:"); - for (int i=0; i> handleGATTServerEvent: %s", - BLEUtils::gattServerEventTypeToString(event).c_str()); +void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); // Invoke the handler for every Service we have. m_serviceMap.handleGATTServerEvent(event, gatts_if, param); - switch(event) { + switch (event) { // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status @@ -220,7 +216,7 @@ void BLEServer::handleGATTServerEvent( m_connId = param->connect.conn_id; // Save the connection id. if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); - m_pServerCallbacks->onConnect(this, param); + m_pServerCallbacks->onConnect(this, param); } m_connectedCount++; // Increment the number of connected devices count. break; @@ -307,9 +303,8 @@ void BLEServer::handleGATTServerEvent( break; } - default: { + default: break; - } } ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); } // handleGATTServerEvent @@ -345,9 +340,9 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { /* * Remove service */ -void BLEServer::removeService(BLEService *service) { +void BLEServer::removeService(BLEService* service) { service->stop(); - service->executeDelete(); + service->executeDelete(); m_serviceMap.removeService(service); } @@ -370,7 +365,7 @@ void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect -void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { +void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 7f40faef..e3362b75 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -42,12 +42,13 @@ class BLEServiceMap { std::string toString(); BLEService* getFirst(); BLEService* getNext(); - void removeService(BLEService *service); + void removeService(BLEService* service); private: std::map m_handleMap; std::map m_uuidMap; std::map::iterator m_iterator; + }; @@ -58,13 +59,13 @@ class BLEServer { public: uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); + BLEService* createService(BLEUUID uuid, uint32_t numHandles = 15, uint8_t inst_id = 0); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); - void removeService(BLEService *service); BLEService* getServiceByUUID(const char* uuid); BLEService* getServiceByUUID(BLEUUID uuid); + void removeService(BLEService* service); private: BLEServer(); @@ -85,9 +86,9 @@ class BLEServer { void createApp(uint16_t appId); uint16_t getConnId(); uint16_t getGattsIf(); - void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void registerApp(); + void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); }; // BLEServer @@ -105,7 +106,7 @@ class BLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the client connection. */ virtual void onConnect(BLEServer* pServer); - virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); + virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param); /** * @brief Handle an existing client disconnection. * @@ -117,6 +118,5 @@ class BLEServerCallbacks { }; // BLEServerCallbacks - #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 340ea560..93472f61 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -61,7 +61,7 @@ BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { * @return N/A. */ -void BLEService::executeCreate(BLEServer *pServer) { +void BLEService::executeCreate(BLEServer* pServer) { //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); //getUUID(); // Needed for a weird bug fix //char x[1000]; @@ -75,11 +75,7 @@ void BLEService::executeCreate(BLEServer *pServer) { srvc_id.is_primary = true; srvc_id.id.inst_id = m_id; srvc_id.id.uuid = *m_uuid.getNative(); - esp_err_t errRc = ::esp_ble_gatts_create_service( - getServer()->getGattsIf(), - &srvc_id, - m_numHandles // The maximum number of handles associated with the service. - ); + esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -101,7 +97,7 @@ void BLEService::executeDelete() { ESP_LOGD(LOG_TAG, ">> executeDelete()"); m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT - esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() ); + esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle()); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -141,20 +137,19 @@ BLEUUID BLEService::getUUID() { * @return Start the service. */ void BLEService::start() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// + // We ask the BLE runtime to start the service and then create each of the characteristics. + // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event + // obtained as a result of calling esp_ble_gatts_create_service(). + // ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); return; } - BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); - while(pCharacteristic != nullptr) { + while (pCharacteristic != nullptr) { m_lastCreatedCharacteristic = pCharacteristic; pCharacteristic->executeCreate(this); @@ -177,13 +172,11 @@ void BLEService::start() { /** * @brief Stop the service. - * @return Stop the service. */ void BLEService::stop() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// + // We ask the BLE runtime to start the service and then create each of the characteristics. + // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event + // obtained as a result of calling esp_ble_gatts_create_service(). ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); @@ -232,10 +225,9 @@ uint16_t BLEService::getHandle() { * @param [in] pCharacteristic A pointer to the characteristic to be added. */ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { -// We maintain a mapping of characteristics owned by this service. These are managed by the -// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic -// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). -// + // We maintain a mapping of characteristics owned by this service. These are managed by the + // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic + // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), @@ -264,7 +256,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { return createCharacteristic(BLEUUID(uuid), properties); } - + /** * @brief Create a new BLE Characteristic associated with this service. @@ -273,7 +265,7 @@ BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t p * @return The new BLE characteristic. */ BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { - BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); + BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties); addCharacteristic(pCharacteristic); return pCharacteristic; } // createCharacteristic @@ -282,13 +274,8 @@ BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t prope /** * @brief Handle a GATTS server event. */ -void BLEService::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - - - switch(event) { +void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + switch (event) { // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status @@ -377,9 +364,8 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_DELETE_EVT - default: { + default: break; - } // Default } // Switch // Invoke the GATTS handler in each of the associated characteristics. diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 93b4b2c6..a4eea23b 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -27,17 +27,13 @@ class BLECharacteristicMap { void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(const char* uuid); BLECharacteristic* getByUUID(BLEUUID uuid); BLECharacteristic* getByHandle(uint16_t handle); BLECharacteristic* getFirst(); BLECharacteristic* getNext(); std::string toString(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); private: std::map m_uuidMap; @@ -57,16 +53,16 @@ class BLEService { BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); void dump(); void executeCreate(BLEServer* pServer); - void executeDelete(); + void executeDelete(); BLECharacteristic* getCharacteristic(const char* uuid); BLECharacteristic* getCharacteristic(BLEUUID uuid); BLEUUID getUUID(); BLEServer* getServer(); void start(); - void stop(); + void stop(); std::string toString(); uint16_t getHandle(); - uint8_t m_id = 0; + uint8_t m_id = 0; private: BLEService(const char* uuid, uint32_t numHandles); @@ -91,12 +87,10 @@ class BLEService { uint32_t m_numHandles; BLECharacteristic* getLastCreatedCharacteristic(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setHandle(uint16_t handle); //void setService(esp_gatt_srvc_id_t srvc_id); + }; // BLEService diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index dd828fae..d8610a85 100644 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -17,9 +17,9 @@ * @return The characteristic. */ BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } - + /** * @brief Return the service by UUID. * @param [in] UUID The UUID to look up the service. @@ -53,8 +53,8 @@ BLEService* BLEServiceMap::getByHandle(uint16_t handle) { * @return N/A. */ void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService *service) { - m_uuidMap.insert(std::pair(service, uuid.toString())); + BLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); } // setByUUID @@ -66,7 +66,7 @@ void BLEServiceMap::setByUUID(BLEUUID uuid, */ void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); + m_handleMap.insert(std::pair(handle, service)); } // setByHandle @@ -86,7 +86,7 @@ std::string BLEServiceMap::toString() { void BLEServiceMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { + esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { myPair.first->handleGATTServerEvent(event, gatts_if, param); @@ -99,9 +99,7 @@ void BLEServiceMap::handleGATTServerEvent( */ BLEService* BLEServiceMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -112,9 +110,7 @@ BLEService* BLEServiceMap::getFirst() { * @return The next service in the map. */ BLEService* BLEServiceMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -124,7 +120,7 @@ BLEService* BLEServiceMap::getNext() { * @brief Removes service from maps. * @return N/A. */ -void BLEServiceMap::removeService(BLEService *service){ +void BLEServiceMap::removeService(BLEService* service) { m_handleMap.erase(service->getHandle()); m_uuidMap.erase(service); } // removeService diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 9ca7cdd7..512c24ea 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -41,7 +41,7 @@ static const char* LOG_TAG = "BLEUUID"; */ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { assert(size > 0); - target+=(size-1); // Point target to the last byte of the target data + target += (size - 1); // Point target to the last byte of the target data while (size > 0) { *target = *source; target--; @@ -80,11 +80,11 @@ BLEUUID::BLEUUID(std::string value) { } else if (value.length() == 16) { m_uuid.len = ESP_UUID_LEN_128; - memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); + memrcpy(m_uuid.uuid.uuid128, (uint8_t*) value.data(), 16); } else if (value.length() == 36) { -// If the length of the string is 36 bytes then we will assume it is a long hex string in -// UUID format. + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. m_uuid.len = ESP_UUID_LEN_128; int vals[16]; sscanf(value.c_str(), "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x", @@ -106,12 +106,10 @@ BLEUUID::BLEUUID(std::string value) { &vals[0] ); - int i; - for (i=0; i<16; i++) { + for (int i = 0; i < 16; i++) { m_uuid.uuid.uuid128[i] = vals[i]; } - } - else { + } else { ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes"); m_valueSet = false; } @@ -136,7 +134,7 @@ BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { } else { memcpy(m_uuid.uuid.uuid128, pData, 16); } - m_valueSet = true; + m_valueSet = true; } // BLEUUID @@ -149,7 +147,6 @@ BLEUUID::BLEUUID(uint16_t uuid) { m_uuid.len = ESP_UUID_LEN_16; m_uuid.uuid.uuid16 = uuid; m_valueSet = true; - } // BLEUUID @@ -194,24 +191,18 @@ BLEUUID::BLEUUID() { * @brief Get the number of bits in this uuid. * @return The number of bits in the UUID. One of 16, 32 or 128. */ -int BLEUUID::bitSize() { - if (m_valueSet == false) { - return 0; - } - switch(m_uuid.len) { - case ESP_UUID_LEN_16: { +uint8_t BLEUUID::bitSize() { + if (!m_valueSet) return 0; + switch (m_uuid.len) { + case ESP_UUID_LEN_16: return 16; - } - case ESP_UUID_LEN_32: { + case ESP_UUID_LEN_32: return 32; - } - case ESP_UUID_LEN_128: { + case ESP_UUID_LEN_128: return 128; - } - default: { + default: ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); return 0; - } } // End of switch } // bitSize @@ -224,9 +215,7 @@ int BLEUUID::bitSize() { */ bool BLEUUID::equals(BLEUUID uuid) { //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str()); - if (m_valueSet == false || uuid.m_valueSet == false) { - return false; - } + if (!m_valueSet || !uuid.m_valueSet) return false; if (uuid.m_uuid.len != m_uuid.len) { return uuid.toString() == toString(); @@ -253,14 +242,14 @@ bool BLEUUID::equals(BLEUUID uuid) { * NNNNNNNN * */ -BLEUUID BLEUUID::fromString(std::string _uuid){ +BLEUUID BLEUUID::fromString(std::string _uuid) { uint8_t start = 0; if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. start = 2; } uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. - if( len == 4) { + if (len == 4) { uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); return BLEUUID(x); } else if (len == 8) { @@ -280,7 +269,7 @@ BLEUUID BLEUUID::fromString(std::string _uuid){ */ esp_bt_uuid_t* BLEUUID::getNative() { //ESP_LOGD(TAG, ">> getNative()") - if (m_valueSet == false) { + if (!m_valueSet) { ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!"); return nullptr; } @@ -299,7 +288,7 @@ BLEUUID BLEUUID::to128() { //ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str()); // If we either don't have a value or are already a 128 bit UUID, nothing further to do. - if (m_valueSet == false || m_uuid.len == ESP_UUID_LEN_128) { + if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) { return *this; } @@ -356,9 +345,7 @@ BLEUUID BLEUUID::to128() { * @return A string representation of the UUID. */ std::string BLEUUID::toString() { - if (m_valueSet == false) { // If we have no value, nothing to format. - return ""; - } + if (!m_valueSet) return ""; // If we have no value, nothing to format. // If the UUIDs are 16 or 32 bit, pad correctly. std::stringstream ss; @@ -386,24 +373,23 @@ std::string BLEUUID::toString() { // // UUID string format: // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP - // ss << std::hex << std::setfill('0') << - std::setw(2) << (int)m_uuid.uuid.uuid128[15] << - std::setw(2) << (int)m_uuid.uuid.uuid128[14] << - std::setw(2) << (int)m_uuid.uuid.uuid128[13] << - std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[11] << - std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[9] << - std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[7] << - std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[5] << - std::setw(2) << (int)m_uuid.uuid.uuid128[4] << - std::setw(2) << (int)m_uuid.uuid.uuid128[3] << - std::setw(2) << (int)m_uuid.uuid.uuid128[2] << - std::setw(2) << (int)m_uuid.uuid.uuid128[1] << - std::setw(2) << (int)m_uuid.uuid.uuid128[0]; + std::setw(2) << (int) m_uuid.uuid.uuid128[15] << + std::setw(2) << (int) m_uuid.uuid.uuid128[14] << + std::setw(2) << (int) m_uuid.uuid.uuid128[13] << + std::setw(2) << (int) m_uuid.uuid.uuid128[12] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[11] << + std::setw(2) << (int) m_uuid.uuid.uuid128[10] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[9] << + std::setw(2) << (int) m_uuid.uuid.uuid128[8] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[7] << + std::setw(2) << (int) m_uuid.uuid.uuid128[6] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[5] << + std::setw(2) << (int) m_uuid.uuid.uuid128[4] << + std::setw(2) << (int) m_uuid.uuid.uuid128[3] << + std::setw(2) << (int) m_uuid.uuid.uuid128[2] << + std::setw(2) << (int) m_uuid.uuid.uuid128[1] << + std::setw(2) << (int) m_uuid.uuid.uuid128[0]; return ss.str(); } // toString diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 53f1a8f2..0dd5f461 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -31,8 +31,8 @@ static const char* LOG_TAG = "BLEUtils"; // Tag for logging. /* -static std::map g_addressMap; -static std::map g_connIdMap; +static std::map g_addressMap; +static std::map g_connIdMap; */ typedef struct { @@ -628,7 +628,7 @@ static std::string gattIdToString(esp_gatt_id_t gattId) { * @brief Convert an esp_ble_addr_type_t to a string representation. */ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { - switch(type) { + switch (type) { case BLE_ADDR_TYPE_PUBLIC: return "BLE_ADDR_TYPE_PUBLIC"; case BLE_ADDR_TYPE_RANDOM: @@ -650,19 +650,19 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { */ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { std::stringstream ss; - if (adFlags & (1<<0)) { + if (adFlags & (1 << 0)) { ss << "[LE Limited Discoverable Mode] "; } - if (adFlags & (1<<1)) { + if (adFlags & (1 << 1)) { ss << "[LE General Discoverable Mode] "; } - if (adFlags & (1<<2)) { + if (adFlags & (1 << 2)) { ss << "[BR/EDR Not Supported] "; } - if (adFlags & (1<<3)) { + if (adFlags & (1 << 3)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Controller)] "; } - if (adFlags & (1<<4)) { + if (adFlags & (1 << 4)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Host)] "; } return ss.str(); @@ -678,82 +678,57 @@ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { * @return A string representation of the type. */ const char* BLEUtils::advTypeToString(uint8_t advType) { - switch(advType) { - case ESP_BLE_AD_TYPE_FLAG: // 0x01 + switch (advType) { + case ESP_BLE_AD_TYPE_FLAG: // 0x01 return "ESP_BLE_AD_TYPE_FLAG"; - - case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 + case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 return "ESP_BLE_AD_TYPE_16SRV_PART"; - - case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 + case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 return "ESP_BLE_AD_TYPE_16SRV_CMPL"; - - case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 + case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 return "ESP_BLE_AD_TYPE_32SRV_PART"; - - case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 + case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 return "ESP_BLE_AD_TYPE_32SRV_CMPL"; - - case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 + case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 return "ESP_BLE_AD_TYPE_128SRV_PART"; - - case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 + case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 return "ESP_BLE_AD_TYPE_128SRV_CMPL"; - - case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 + case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 return "ESP_BLE_AD_TYPE_NAME_SHORT"; - - case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 + case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 return "ESP_BLE_AD_TYPE_NAME_CMPL"; - - case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a + case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a return "ESP_BLE_AD_TYPE_TX_PWR"; - - case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b + case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b return "ESP_BLE_AD_TYPE_DEV_CLASS"; - - case ESP_BLE_AD_TYPE_SM_TK: // 0x10 + case ESP_BLE_AD_TYPE_SM_TK: // 0x10 return "ESP_BLE_AD_TYPE_SM_TK"; - - case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 + 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 + case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 return "ESP_BLE_AD_TYPE_INT_RANGE"; - - case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14 + 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 + 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 + case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 return "ESP_BLE_AD_TYPE_SERVICE_DATA"; - - case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 + case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 return "ESP_BLE_AD_TYPE_PUBLIC_TARGET"; - - case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 + case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 return "ESP_BLE_AD_TYPE_RANDOM_TARGET"; - - case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 + case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 return "ESP_BLE_AD_TYPE_APPEARANCE"; - - case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a + 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 + case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 return "ESP_BLE_AD_TYPE_32SERVICE_DATA"; - - case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 + 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"; - default: ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType); return ""; @@ -768,8 +743,7 @@ esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) { return retGattId; } -esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, - bool is_primary) { +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; @@ -784,29 +758,26 @@ esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, * @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; - } +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); + target = (uint8_t*) malloc(length * 2 + 1); if (target == nullptr) { ESP_LOGE(LOG_TAG, "buildHexData: malloc failed"); return nullptr; } } - char *startOfData = (char *)target; + char* startOfData = (char*) target; - int i; - for (i=0; iadv_data_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT - - // // ESP_GAP_BLE_AUTH_CMPL_EVT // // auth_cmpl @@ -1150,7 +1081,6 @@ void BLEUtils::dumpGapEvent( // - 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: { ESP_LOGD(LOG_TAG, "[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(), @@ -1163,38 +1093,26 @@ void BLEUtils::dumpGapEvent( 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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { ESP_LOGD(LOG_TAG, "[bd_addr: %s, passkey: %d]", BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(), @@ -1202,15 +1120,12 @@ void BLEUtils::dumpGapEvent( 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: { ESP_LOGD(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", param->read_rssi_cmpl.status, @@ -1220,20 +1135,15 @@ void BLEUtils::dumpGapEvent( 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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_RESULT_EVT // // scan_rst: @@ -1248,9 +1158,8 @@ void BLEUtils::dumpGapEvent( // - num_resps // - adv_data_len // - scan_rsp_len - // case ESP_GAP_BLE_SCAN_RESULT_EVT: { - switch(param->scan_rst.search_evt) { + switch (param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { ESP_LOGD(LOG_TAG, "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), @@ -1276,29 +1185,21 @@ void BLEUtils::dumpGapEvent( 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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + ESP_LOGD(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); + ESP_LOGD(LOG_TAG, "[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 @@ -1308,20 +1209,15 @@ void BLEUtils::dumpGapEvent( 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: { ESP_LOGD(LOG_TAG, "[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 @@ -1332,7 +1228,6 @@ void BLEUtils::dumpGapEvent( // - uint16_t latency // - uint16_t conn_int // - uint16_t timeout - // case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", param->update_conn_params.status, @@ -1346,16 +1241,12 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT - - // // ESP_GAP_BLE_SEC_REQ_EVT - // case ESP_GAP_BLE_SEC_REQ_EVT: { ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); break; } // ESP_GAP_BLE_SEC_REQ_EVT - default: { ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; @@ -1375,10 +1266,9 @@ void BLEUtils::dumpGattClientEvent( 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; + //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); - switch(event) { - // + switch (event) { // ESP_GATTC_CLOSE_EVT // // close: @@ -1386,7 +1276,6 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda // - esp_gatt_conn_reason_t reason - // case ESP_GATTC_CLOSE_EVT: { ESP_LOGD(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->close.status).c_str(), @@ -1395,7 +1284,6 @@ void BLEUtils::dumpGattClientEvent( break; } - // // ESP_GATTC_CONNECT_EVT // // connect: @@ -1410,7 +1298,6 @@ void BLEUtils::dumpGattClientEvent( break; } - // // ESP_GATTC_DISCONNECT_EVT // // disconnect: @@ -1426,7 +1313,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_DISCONNECT_EVT - // // ESP_GATTC_GET_CHAR_EVT // // get_char: @@ -1435,7 +1321,6 @@ void BLEUtils::dumpGattClientEvent( // - 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: { @@ -1465,7 +1350,6 @@ void BLEUtils::dumpGattClientEvent( } // ESP_GATTC_GET_CHAR_EVT */ - // // ESP_GATTC_NOTIFY_EVT // // notify @@ -1488,7 +1372,6 @@ void BLEUtils::dumpGattClientEvent( break; } - // // ESP_GATTC_OPEN_EVT // // open: @@ -1506,8 +1389,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_OPEN_EVT - - // // ESP_GATTC_READ_CHAR_EVT // // Callback to indicate that requested data that we wanted to read is now available. @@ -1530,7 +1411,7 @@ void BLEUtils::dumpGattClientEvent( 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); + char* pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); ESP_LOGD(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); free(pHexData); */ @@ -1538,14 +1419,11 @@ void BLEUtils::dumpGattClientEvent( 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: { ESP_LOGD(LOG_TAG, "[status: %s, app_id: 0x%x]", BLEUtils::gattStatusToString(evtParam->reg.status).c_str(), @@ -1553,8 +1431,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_REG_EVT - - // // ESP_GATTC_REG_FOR_NOTIFY_EVT // // reg_for_notify: @@ -1569,14 +1445,11 @@ void BLEUtils::dumpGattClientEvent( 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: { ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(), @@ -1584,8 +1457,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_SEARCH_CMPL_EVT - - // // ESP_GATTC_SEARCH_RES_EVT // // search_res: @@ -1593,7 +1464,6 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t start_handle // - uint16_t end_handle // - esp_gatt_id_t srvc_id - // case ESP_GATTC_SEARCH_RES_EVT: { ESP_LOGD(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", evtParam->search_res.conn_id, @@ -1605,8 +1475,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_SEARCH_RES_EVT - - // // ESP_GATTC_WRITE_CHAR_EVT // // write: @@ -1614,7 +1482,6 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - uint16_t handle // - uint16_t offset - // case ESP_GATTC_WRITE_CHAR_EVT: { ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]", BLEUtils::gattStatusToString(evtParam->write.status).c_str(), @@ -1647,7 +1514,7 @@ void BLEUtils::dumpGattServerEvent( esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam) { ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - switch(event) { + switch (event) { case ESP_GATTS_ADD_CHAR_DESCR_EVT: { ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", @@ -1687,7 +1554,6 @@ void BLEUtils::dumpGattServerEvent( // conf: // - esp_gatt_status_t status – The status code. // - uint16_t conn_id – The connection used. - // case ESP_GATTS_CONF_EVT: { ESP_LOGD(LOG_TAG, "[status: %s, conn_id: 0x%.2x]", gattStatusToString(evtParam->conf.status).c_str(), @@ -1733,22 +1599,21 @@ void BLEUtils::dumpGattServerEvent( // - 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) { + switch (evtParam->exec_write.exec_write_flag) { case ESP_GATT_PREP_WRITE_EXEC: { - pWriteFlagText = (char*)"WRITE"; + pWriteFlagText = (char*) "WRITE"; break; } case ESP_GATT_PREP_WRITE_CANCEL: { - pWriteFlagText = (char*)"CANCEL"; + pWriteFlagText = (char*) "CANCEL"; break; } default: - pWriteFlagText = (char*)""; + pWriteFlagText = (char*) ""; break; } @@ -1800,7 +1665,6 @@ void BLEUtils::dumpGattServerEvent( // start: // - esp_gatt_status_t status // - uint16_t service_handle - // case ESP_GATTS_START_EVT: { ESP_LOGD(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", gattStatusToString(evtParam->start.status).c_str(), @@ -1821,7 +1685,6 @@ void BLEUtils::dumpGattServerEvent( // - 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: { ESP_LOGD(LOG_TAG, "[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, @@ -1851,7 +1714,7 @@ void BLEUtils::dumpGattServerEvent( * @return The event type as a string. */ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { - switch(eventType) { + switch (eventType) { case ESP_BLE_EVT_CONN_ADV: return "ESP_BLE_EVT_CONN_ADV"; case ESP_BLE_EVT_CONN_DIR_ADV: @@ -1876,89 +1739,61 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { * @return A string representation of the event type. */ const char* BLEUtils::gapEventToString(uint32_t eventType) { - switch(eventType) { + switch (eventType) { 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 */ + 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. */ + 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 */ + 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 */ + 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 */ + 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 */ + 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 */ + 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 */ + 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 */ + 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 */ + 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"; - - default: ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; @@ -1967,7 +1802,7 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { - const characteristicMap_t *p = g_characteristicsMappings; + const characteristicMap_t* p = g_characteristicsMappings; while (strlen(p->name) > 0) { if (p->assignedNumber == characteristicUUID) { return std::string(p->name); @@ -1984,7 +1819,7 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID * @return The string representation of a descriptor UUID. */ std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { - gattdescriptor_t* p = (gattdescriptor_t *)g_descriptor_ids; + gattdescriptor_t* p = (gattdescriptor_t*) g_descriptor_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == descriptorUUID) { return std::string(p->name); @@ -2020,7 +1855,7 @@ std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { std::string BLEUtils::gattServiceToString(uint32_t serviceId) { - gattService_t* p = (gattService_t *)g_gattServices; + gattService_t* p = (gattService_t*) g_gattServices; while (strlen(p->name) > 0) { if (p->assignedNumber == serviceId) { return std::string(p->name); @@ -2038,7 +1873,7 @@ std::string BLEUtils::gattServiceToString(uint32_t serviceId) { * @return A string representation of the status. */ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { - switch(status) { + switch (status) { case ESP_GATT_OK: return "ESP_GATT_OK"; case ESP_GATT_INVALID_HANDLE: @@ -2133,11 +1968,11 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { std::string BLEUtils::getMember(uint32_t memberId) { - member_t* p = (member_t *)members_ids; + member_t* p = (member_t*) members_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == memberId) { - return std::string(p->name); + return std::string(p->name); } p++; } @@ -2150,7 +1985,7 @@ std::string BLEUtils::getMember(uint32_t memberId) { * @return The search event type as a string. */ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { - switch(searchEvt) { + switch (searchEvt) { case ESP_GAP_SEARCH_INQ_RES_EVT: return "ESP_GAP_SEARCH_INQ_RES_EVT"; case ESP_GAP_SEARCH_INQ_CMPL_EVT: diff --git a/cpp_utils/BLEUtils.h b/cpp_utils/BLEUtils.h index b2baee53..7981691c 100644 --- a/cpp_utils/BLEUtils.h +++ b/cpp_utils/BLEUtils.h @@ -23,12 +23,12 @@ class BLEUtils { static const char* addressTypeToString(esp_ble_addr_type_t type); static std::string adFlagsToString(uint8_t adFlags); static const char* advTypeToString(uint8_t advType); - static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); - static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true); static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); static std::string buildPrintData(uint8_t* source, size_t length); static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); static const char* devTypeToString(esp_bt_dev_type_t type); + static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id = 0); + static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true); static void dumpGapEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); @@ -47,7 +47,7 @@ class BLEUtils { static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); - static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement); + static std::string gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement); static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); diff --git a/cpp_utils/BLEValue.cpp b/cpp_utils/BLEValue.cpp index 49818e27..aac0f500 100644 --- a/cpp_utils/BLEValue.cpp +++ b/cpp_utils/BLEValue.cpp @@ -42,7 +42,7 @@ void BLEValue::addPart(std::string part) { */ void BLEValue::addPart(uint8_t* pData, size_t length) { ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length); - m_accumulation += std::string((char *)pData, length); + m_accumulation += std::string((char*) pData, length); } // addPart @@ -65,9 +65,7 @@ void BLEValue::cancel() { void BLEValue::commit() { ESP_LOGD(LOG_TAG, ">> commit"); // If there is nothing to commit, do nothing. - if (m_accumulation.length() == 0) { - return; - } + if (m_accumulation.length() == 0) return; setValue(m_accumulation); m_accumulation = ""; m_readOffset = 0; @@ -79,7 +77,7 @@ void BLEValue::commit() { * @return A pointer to the data. */ uint8_t* BLEValue::getData() { - return (uint8_t*)m_value.data(); + return (uint8_t*) m_value.data(); } @@ -132,11 +130,8 @@ void BLEValue::setValue(std::string value) { * @param [in] The length of the new current value. */ void BLEValue::setValue(uint8_t* pData, size_t length) { - m_value = std::string((char*)pData, length); + m_value = std::string((char*) pData, length); } // setValue - - - #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEValue.h b/cpp_utils/BLEValue.h index 92a7f9a4..5df904c1 100644 --- a/cpp_utils/BLEValue.h +++ b/cpp_utils/BLEValue.h @@ -17,13 +17,13 @@ class BLEValue { public: BLEValue(); - void addPart(std::string part); - void addPart(uint8_t* pData, size_t length); - void cancel(); - void commit(); - uint8_t* getData(); - size_t getLength(); - uint16_t getReadOffset(); + void addPart(std::string part); + void addPart(uint8_t* pData, size_t length); + void cancel(); + void commit(); + uint8_t* getData(); + size_t getLength(); + uint16_t getReadOffset(); std::string getValue(); void setReadOffset(uint16_t readOffset); void setValue(std::string value); @@ -33,6 +33,7 @@ class BLEValue { std::string m_accumulation; uint16_t m_readOffset; std::string m_value; + }; #endif // CONFIG_BT_ENABLED #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ diff --git a/cpp_utils/BLEXML/Characteristics/code/run.sh b/cpp_utils/BLEXML/Characteristics/code/run.sh index 77f193fe..f0c7930a 100755 --- a/cpp_utils/BLEXML/Characteristics/code/run.sh +++ b/cpp_utils/BLEXML/Characteristics/code/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash BASE_URL="https://www.bluetooth.com/api/gatt/XmlFile?xmlFileName=" RESULT=characteristics.json COUNT=0 @@ -17,4 +17,4 @@ do COUNT=$(expr ${COUNT} + 1) done echo -e "\n]\n" >> ${RESULT} -echo "done" \ No newline at end of file +echo "done" diff --git a/cpp_utils/BLEXML/Services/code/run.sh b/cpp_utils/BLEXML/Services/code/run.sh index bfa084d0..6716a53a 100755 --- a/cpp_utils/BLEXML/Services/code/run.sh +++ b/cpp_utils/BLEXML/Services/code/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash BASE_URL="https://www.bluetooth.com/api/gatt/XmlFile?xmlFileName=" RESULT=services.json COUNT=0 @@ -17,4 +17,4 @@ do COUNT=$(expr ${COUNT} + 1) done echo -e "\n]\n" >> ${RESULT} -echo "done" \ No newline at end of file +echo "done" diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index b204045b..18914ab5 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -85,7 +85,7 @@ int NVS::get(std::string key, std::string* result, bool isBlob) { return rc; } } - char *data = (char *)malloc(length); + char* data = (char*) malloc(length); if (isBlob) { ::nvs_get_blob(m_handle, key.c_str(), data, &length); } else { diff --git a/cpp_utils/CPPNVS.h b/cpp_utils/CPPNVS.h index 6afbc166..9155891b 100644 --- a/cpp_utils/CPPNVS.h +++ b/cpp_utils/CPPNVS.h @@ -21,15 +21,17 @@ class NVS { void erase(); void erase(std::string key); - int get(std::string key, std::string* result, bool isBlob=false); - int get(std::string key, uint8_t* result, size_t &length); + int get(std::string key, std::string* result, bool isBlob = false); + int get(std::string key, uint8_t* result, size_t& length); int get(std::string key, uint32_t& value); - void set(std::string key, std::string data, bool isBlob=false); + void set(std::string key, std::string data, bool isBlob = false); void set(std::string key, uint32_t value); void set(std::string key, uint8_t* data, size_t length); + private: std::string m_name; nvs_handle m_handle; + }; #endif /* COMPONENTS_CPP_UTILS_CPPNVS_H_ */ diff --git a/cpp_utils/Console.cpp b/cpp_utils/Console.cpp index c88d8372..c0f5655f 100644 --- a/cpp_utils/Console.cpp +++ b/cpp_utils/Console.cpp @@ -42,7 +42,7 @@ ArgTable::~ArgTable() { */ int size = m_argTableEntries.size(); m_argtable = new void*[size + 1]; - int i=0; + int i = 0; for (auto it = m_argTableEntries.begin(); it != m_argTableEntries.end(); ++it) { m_argtable[i] = it->second->getEntry(); i++; @@ -54,42 +54,42 @@ ArgTable::~ArgTable() { ArgTableEntry_Date ArgTable::addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary) { ArgTableEntry_Date* pDate = new ArgTableEntry_Date(shortopts, longopts, glossary); - m_argTableEntries.push_back(std::make_pair(name, pDate)); + m_argTableEntries.push_back(std::make_pair(name, pDate)); return *pDate; } // ArgTable#addDate ArgTableEntry_Double ArgTable::addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary) { ArgTableEntry_Double* pDouble = new ArgTableEntry_Double(shortopts, longopts, glossary); - m_argTableEntries.push_back(std::make_pair(name, pDouble)); + m_argTableEntries.push_back(std::make_pair(name, pDouble)); return *pDouble; } // ArgTable#addDouble ArgTableEntry_File ArgTable::addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary) { ArgTableEntry_File* pFile = new ArgTableEntry_File(shortopts, longopts, glossary); - m_argTableEntries.push_back(std::make_pair(name, pFile)); + m_argTableEntries.push_back(std::make_pair(name, pFile)); return *pFile; } // ArgTable#addFile ArgTableEntry_Int ArgTable::addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary) { ArgTableEntry_Int* pInt = new ArgTableEntry_Int(shortopts, longopts, glossary); - m_argTableEntries.push_back(std::make_pair(name, pInt)); + m_argTableEntries.push_back(std::make_pair(name, pInt)); return *pInt; } // ArgTable#addInt ArgTableEntry_Lit ArgTable::addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary) { ArgTableEntry_Lit* pLit = new ArgTableEntry_Lit(shortopts, longopts, glossary); - m_argTableEntries.push_back(std::make_pair(name, pLit)); + m_argTableEntries.push_back(std::make_pair(name, pLit)); return *pLit; } // ArgTable#addLit ArgTableEntry_String ArgTable::addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max) { ArgTableEntry_String* pStr = new ArgTableEntry_String(shortopts, longopts, glossary, min, max); - m_argTableEntries.push_back(std::make_pair(name, pStr)); + m_argTableEntries.push_back(std::make_pair(name, pStr)); return *pStr; } // ArgTable#addString @@ -147,46 +147,56 @@ ArgTableEntry_Double::ArgTableEntry_Double(std::string shortopts, std::string lo m_type = ArgType_t::DBL; } + int ArgTableEntry_Double::getCount() { return m_argDbl->count; } + ArgTableEntry_File::ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary) { m_argFile = arg_filen(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); - m_type = ArgType_t::FILE; + m_type = ArgType_t::FILE; } + int ArgTableEntry_File::getCount() { return m_argFile->count; } + ArgTableEntry_Int::ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary) { m_argInt = arg_intn(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); m_type = ArgType_t::INT; } + int ArgTableEntry_Int::getCount() { return m_argInt->count; } + ArgTableEntry_Lit::ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary) { m_argLit = arg_litn(shortopts.c_str(), longopts.c_str(), 0, 1, glossary.c_str()); m_type = ArgType_t::LIT; } + int ArgTableEntry_Lit::getCount() { return m_argLit->count; } + int ArgTableEntry_Regex::getCount() { return m_argRex->count; } + ArgTableEntry_String::ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max) { m_argStr = arg_strn(shortopts.c_str(), longopts.c_str(), "", min, max, glossary.c_str()); m_type = ArgType_t::STR; } + int ArgTableEntry_String::getCount() { return m_argStr->count; } @@ -195,6 +205,7 @@ int ArgTableEntry_String::getCount() { Console::Console() { } + Console::~Console() { } diff --git a/cpp_utils/Console.h b/cpp_utils/Console.h index 2ce40fa2..a94f63a1 100644 --- a/cpp_utils/Console.h +++ b/cpp_utils/Console.h @@ -52,7 +52,7 @@ class ArgTableEntry_Int : public ArgTableEntry_Generic { public: ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary); int getCount(); - int getValue(int index=0); + int getValue(int index = 0); void* getEntry() { return m_argInt; } @@ -65,7 +65,7 @@ class ArgTableEntry_Double : public ArgTableEntry_Generic { public: ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary); int getCount(); - double getValue(int index=0); + double getValue(int index = 0); void* getEntry() { return m_argDbl; } @@ -78,7 +78,7 @@ class ArgTableEntry_String : public ArgTableEntry_Generic { public: ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max); int getCount(); - std::string getValue(int index=0); + std::string getValue(int index = 0); void* getEntry() { return m_argStr; } @@ -90,7 +90,7 @@ class ArgTableEntry_Regex : public ArgTableEntry_Generic { struct arg_rex* m_argRex; public: int getCount(); - std::string getValue(int index=0); + std::string getValue(int index = 0); void* getEntry() { return m_argRex; } @@ -103,9 +103,9 @@ class ArgTableEntry_File : public ArgTableEntry_Generic { public: ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary); int getCount(); - std::string getFilename(int index=0); - std::string getBasename(int index=0); - std::string getExtension(int index=0); + std::string getFilename(int index = 0); + std::string getBasename(int index = 0); + std::string getExtension(int index = 0); void* getEntry() { return m_argFile; } @@ -118,7 +118,7 @@ class ArgTableEntry_Date : public ArgTableEntry_Generic { public: ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary); int getCount(); - struct tm* getValue(int index=0); + struct tm* getValue(int index = 0); void* getEntry() { return m_argDate; } diff --git a/cpp_utils/FTPCallbacks.cpp b/cpp_utils/FTPCallbacks.cpp index d88c2300..69e462c9 100644 --- a/cpp_utils/FTPCallbacks.cpp +++ b/cpp_utils/FTPCallbacks.cpp @@ -24,7 +24,7 @@ void FTPFileCallbacks::onStoreStart(std::string fileName) { */ size_t FTPFileCallbacks::onStoreData(uint8_t* data, size_t size) { ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onStoreData: size=%d", size); - m_storeFile.write((char *)data, size); // Store data received. + m_storeFile.write((char*) data, size); // Store data received. ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreData: size=%d", size); return size; } // FTPFileCallbacks#onStoreData @@ -65,7 +65,7 @@ void FTPFileCallbacks::onRetrieveStart(std::string fileName) { */ size_t FTPFileCallbacks::onRetrieveData(uint8_t* data, size_t size) { ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveData"); - m_retrieveFile.read((char *)data, size); + m_retrieveFile.read((char*) data, size); size_t readSize = m_retrieveFile.gcount(); m_byteCount += readSize; ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveData: sizeRead=%d", readSize); @@ -88,14 +88,11 @@ void FTPFileCallbacks::onRetrieveEnd() { * @return a list of files in the file system. */ std::string FTPFileCallbacks::onDir() { - DIR* dir = opendir(FTPServer::getCurrentDirectory().c_str()); std::stringstream ss; - while(1) { + while (true) { struct dirent* pDirentry = readdir(dir); - if (pDirentry == nullptr) { - break; - } + if (pDirentry == nullptr) break; ss << pDirentry->d_name << "\r\n"; } closedir(dir); @@ -131,7 +128,7 @@ void FTPCallbacks::onRetrieveStart(std::string fileName) { } // FTPCallbacks#onRetrieveStart -size_t FTPCallbacks::onRetrieveData(uint8_t *data, size_t size) { +size_t FTPCallbacks::onRetrieveData(uint8_t* data, size_t size) { ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveData"); ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveData: 0"); return 0; diff --git a/cpp_utils/FTPServer.cpp b/cpp_utils/FTPServer.cpp index f8d006fd..63cf6a9c 100644 --- a/cpp_utils/FTPServer.cpp +++ b/cpp_utils/FTPServer.cpp @@ -22,24 +22,24 @@ static const char* LOG_TAG = "FTPServer"; // trim from start (in place) static void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { - return !std::isspace(ch); - })); + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); } // ltrim // trim from end (in place) static void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { - return !std::isspace(ch); - }).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); } // rtrim // trim from both ends (in place) static void trim(std::string &s) { - ltrim(s); - rtrim(s); + ltrim(s); + rtrim(s); } // trim @@ -128,13 +128,13 @@ std::string FTPServer::listenPassive() { struct sockaddr_in clientAddrInfo; unsigned int addrInfoSize = sizeof(clientAddrInfo); - getsockname(m_clientSocket, (struct sockaddr*)&clientAddrInfo, &addrInfoSize); + getsockname(m_clientSocket, (struct sockaddr*) &clientAddrInfo, &addrInfoSize); struct sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(0); - int rc = bind(m_passiveSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + int rc = bind(m_passiveSocket, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); if (rc == -1) { ESP_LOGD(LOG_TAG, "bind: %s", strerror(errno)); } @@ -150,10 +150,9 @@ std::string FTPServer::listenPassive() { ESP_LOGD(LOG_TAG, "getsockname: %s", strerror(errno)); } - std::stringstream ss; - ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << - "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << + ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << "," << ((clientAddrInfo.sin_addr.s_addr >> 16) & 0xff) << "," << ((clientAddrInfo.sin_addr.s_addr >> 24) & 0xff) << "," << ((serverAddress.sin_port >> 0) & 0xff) << @@ -203,7 +202,7 @@ void FTPServer::onList(std::istringstream& ss) { sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. if (m_callbacks != nullptr) { std::string dirString = m_callbacks->onDir(); - sendData((uint8_t *)dirString.data(), dirString.length()); + sendData((uint8_t*) dirString.data(), dirString.length()); } closeData(); sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. @@ -211,7 +210,7 @@ void FTPServer::onList(std::istringstream& ss) { } // FTPServer#onList -void FTPServer::onMkd(std::istringstream &ss) { +void FTPServer::onMkd(std::istringstream& ss) { std::string path; ss >> path; ESP_LOGD(LOG_TAG, ">> onMkd: path=%s", path.c_str()); @@ -247,9 +246,9 @@ void FTPServer::onPort(std::istringstream& ss) { char c; uint16_t h1, h2, h3, h4, p1, p2; ss >> h1 >> c >> h2 >> c >> h3 >> c >> h4 >> c >> p1 >> c >> p2; - m_dataPort = p1*256 + p2; + m_dataPort = p1 * 256 + p2; ESP_LOGD(LOG_TAG, "%d.%d.%d.%d %d", h1, h2, h3, h4, m_dataPort); - m_dataIp = h1<<24 | h2<<16 | h3<<8 | h4; + m_dataIp = h1 << 24 | h2 << 16 | h3 << 8 | h4; sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. m_isPassive = false; @@ -351,7 +350,6 @@ void FTPServer::onQuit(std::istringstream& ss) { * @param ss The parameter stream. */ void FTPServer::onRetr(std::istringstream& ss) { - // We open a data connection back to the client. We then invoke the callback to indicate that we have // started a retrieve operation. We call the retrieve callback to request the next chunk of data and // transmit this down the data connection. We repeat this until there is no more data to send at which @@ -362,12 +360,11 @@ void FTPServer::onRetr(std::istringstream& ss) { ss >> fileName; uint8_t data[m_chunkSize]; - if (m_callbacks != nullptr) { try { m_callbacks->onRetrieveStart(fileName); - } catch(FTPServer::FileException& e) { - sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + } catch (FTPServer::FileException& e) { + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. ESP_LOGD(LOG_TAG, "<< onRetr: Returned 550 to client."); return; } @@ -377,7 +374,7 @@ void FTPServer::onRetr(std::istringstream& ss) { openData(); if (m_callbacks != nullptr) { int readSize = m_callbacks->onRetrieveData(data, m_chunkSize); - while(readSize > 0) { + while (readSize > 0) { sendData(data, readSize); readSize = m_callbacks->onRetrieveData(data, m_chunkSize); } @@ -432,11 +429,7 @@ void FTPServer::onType(std::istringstream& ss) { ESP_LOGD(LOG_TAG, ">> onType"); std::string type; ss >> type; - if (type.compare("I") == 0) { - m_isImage = true; - } else { - m_isImage = false; - } + m_isImage = (type.compare("I") == 0); sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. ESP_LOGD(LOG_TAG, "<< onType: isImage=%d", m_isImage); } // FTPServer#onType @@ -455,15 +448,10 @@ void FTPServer::onType(std::istringstream& ss) { void FTPServer::onUser(std::istringstream& ss) { // When we receive a user command, we next want to know if we should ask for a password. If the m_loginRequired // flag is set then we do indeed want a password and will send the response that we wish one. - std::string userName; ss >> userName; ESP_LOGD(LOG_TAG, ">> onUser: userName=%s", userName.c_str()); - if (m_loginRequired) { - sendResponse(FTPServer::RESPONSE_331_PASSWORD_REQUIRED); - } else { - sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. - } + sendResponse(m_loginRequired ? FTPServer::RESPONSE_331_PASSWORD_REQUIRED : FTPServer::RESPONSE_200_COMMAND_OK); m_suppliedUserid = userName; // Save the username that was supplied. ESP_LOGD(LOG_TAG, "<< onUser"); } // FTPServer#onUser @@ -500,7 +488,7 @@ bool FTPServer::openData() { return false; } closePassive(); - } else { + } else { // Handle an active connection ... here we connect to the client. m_dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -526,24 +514,20 @@ void FTPServer::processCommand() { sendResponse(FTPServer::RESPONSE_220_SERVICE_READY); // Service ready. ESP_LOGD(LOG_TAG, ">> FTPServer::processCommand"); m_lastCommand = ""; - while(1) { + while (true) { std::string line = ""; char currentChar; char lastChar = '\0'; int rc = recv(m_clientSocket, ¤tChar, 1, 0); - while(rc != -1 && rc!=0) { + while (rc != -1 && rc != 0) { line += currentChar; - if (lastChar == '\r' && currentChar == '\n') { - break; - } - //printf("%c\n", currentChar); + if (lastChar == '\r' && currentChar == '\n') break; +// printf("%c\n", currentChar); lastChar = currentChar; rc = recv(m_clientSocket, ¤tChar, 1, 0); } // End while we are waiting for a line. - if (rc == 0 || rc == -1) { // If we didn't get a line or an error, then we have finished processing commands. - break; - } + if (rc == 0 || rc == -1) break; // If we didn't get a line or an error, then we have finished processing commands. std::string command; std::istringstream ss(line); @@ -553,58 +537,58 @@ void FTPServer::processCommand() { // We now have a command to process. ESP_LOGD(LOG_TAG, "Command: \"%s\"", command.c_str()); - if (command.compare("USER")==0) { + if (command.compare("USER") == 0) { onUser(ss); } - else if (command.compare("PASS")==0) { + else if (command.compare("PASS") == 0) { onPass(ss); } else if (m_loginRequired && !m_isAuthenticated) { sendResponse(RESPONSE_530_NOT_LOGGED_IN); } - else if (command.compare("PASV")==0) { + else if (command.compare("PASV") == 0) { onPasv(ss); } - else if (command.compare("SYST")==0) { + else if (command.compare("SYST") == 0) { onSyst(ss); } - else if (command.compare("PORT")==0) { + else if (command.compare("PORT") == 0) { onPort(ss); } - else if (command.compare("LIST")==0) { + else if (command.compare("LIST") == 0) { onList(ss); } - else if (command.compare("TYPE")==0) { + else if (command.compare("TYPE") == 0) { onType(ss); } - else if (command.compare("RETR")==0) { + else if (command.compare("RETR") == 0) { onRetr(ss); } - else if (command.compare("QUIT")==0) { + else if (command.compare("QUIT") == 0) { onQuit(ss); } - else if (command.compare("AUTH")==0) { + else if (command.compare("AUTH") == 0) { onAuth(ss); } - else if (command.compare("STOR")==0) { + else if (command.compare("STOR") == 0) { onStor(ss); } - else if (command.compare("PWD")==0) { + else if (command.compare("PWD") == 0) { onPWD(ss); } - else if (command.compare("MKD")==0) { + else if (command.compare("MKD") == 0) { onMkd(ss); } - else if (command.compare("XMKD")==0) { + else if (command.compare("XMKD") == 0) { onXmkd(ss); } - else if (command.compare("RMD")==0) { + else if (command.compare("RMD") == 0) { onRmd(ss); } - else if (command.compare("XRMD")==0) { + else if (command.compare("XRMD") == 0) { onXrmd(ss); } - else if (command.compare("CWD")==0) { + else if (command.compare("CWD") == 0) { onCwd(ss); } else { @@ -637,11 +621,9 @@ void FTPServer::receiveFile(std::string fileName) { sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. uint8_t buf[m_chunkSize]; uint32_t totalSizeRead = 0; - while(1) { + while (true) { int rc = recv(m_dataSocket, &buf, m_chunkSize, 0); - if (rc <= 0) { - break; - } + if (rc <= 0) break; if (m_callbacks != nullptr) { m_callbacks->onRetrieveData(buf, rc); } @@ -789,7 +771,7 @@ void FTPServer::start() { if (rc == -1) { ESP_LOGD(LOG_TAG, "listen: %s", strerror(errno)); } - while(1) { + while (true) { waitForFTPClient(); processCommand(); } @@ -804,7 +786,7 @@ int FTPServer::waitForFTPClient() { struct sockaddr_in clientAddress; socklen_t clientAddressLength = sizeof(clientAddress); - m_clientSocket = accept(m_serverSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + m_clientSocket = accept(m_serverSocket, (struct sockaddr*) &clientAddress, &clientAddressLength); char ipAddr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddr, sizeof(ipAddr)); @@ -812,7 +794,7 @@ int FTPServer::waitForFTPClient() { struct sockaddr_in socketAddressInfo; unsigned int socketAddressInfoSize = sizeof(socketAddressInfo); - getsockname(m_clientSocket, (struct sockaddr*)&socketAddressInfo, &socketAddressInfoSize); + getsockname(m_clientSocket, (struct sockaddr*) &socketAddressInfo, &socketAddressInfoSize); inet_ntop(AF_INET, &socketAddressInfo.sin_addr, ipAddr, sizeof(ipAddr)); ESP_LOGD(LOG_TAG, "Connected at %s [%d]", ipAddr, socketAddressInfo.sin_port); diff --git a/cpp_utils/FTPServer.h b/cpp_utils/FTPServer.h index 7dc56f13..5736201d 100644 --- a/cpp_utils/FTPServer.h +++ b/cpp_utils/FTPServer.h @@ -24,28 +24,63 @@ class FTPCallbacks { virtual void onRetrieveEnd(); virtual std::string onDir(); virtual ~FTPCallbacks(); + }; /** * An implementation of FTPCallbacks that uses Posix File I/O to perform file access. */ class FTPFileCallbacks : public FTPCallbacks { +public: + void onStoreStart(std::string fileName) override; // Called for a STOR request. + size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. + void onStoreEnd() override; // Called at the end of a STOR request. + void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. + size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. + void onRetrieveEnd() override; // Called when we have retrieved all the data. + std::string onDir() override; // Called to retrieve all the directory entries. + private: - std::ofstream m_storeFile; // File used to store data from the client. + std::ofstream m_storeFile; // File used to store data from the client. std::ifstream m_retrieveFile; // File used to retrieve data for the client. - uint32_t m_byteCount; // Count of bytes sent over wire. -public: - void onStoreStart(std::string fileName) override; // Called for a STOR request. - size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. - void onStoreEnd() override; // Called at the end of a STOR request. - void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. - size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. - void onRetrieveEnd() override; // Called when we have retrieved all the data. - std::string onDir() override; // Called to retrieve all the directory entries. + uint32_t m_byteCount; // Count of bytes sent over wire. + }; class FTPServer { +public: + FTPServer(); + virtual ~FTPServer(); + void setCredentials(std::string userid, std::string password); + void start(); + void setPort(uint16_t port); + void setCallbacks(FTPCallbacks* pFTPCallbacks); + static std::string getCurrentDirectory(); + class FileException: public std::exception { + }; + + // Response codes. + static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; + static const int RESPONSE_200_COMMAND_OK = 200; + static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; + static const int RESPONSE_212_DIRECTORY_STATUS = 212; + static const int RESPONSE_213_FILE_STATUS = 213; + static const int RESPONSE_214_HELP_MESSAGE = 214; + static const int RESPONSE_220_SERVICE_READY = 220; + static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; + static const int RESPONSE_230_USER_LOGGED_IN = 230; + static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; + static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; + static const int RESPONSE_331_PASSWORD_REQUIRED = 331; + static const int RESPONSE_332_NEED_ACCOUNT = 332; + static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; + static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; + static const int RESPONSE_503_BAD_SEQUENCE = 503; + static const int RESPONSE_530_NOT_LOGGED_IN = 530; + static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; + static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; + private: int m_serverSocket; // The socket the FTP server is listening on. int m_clientSocket; // The current client socket. @@ -98,40 +133,6 @@ class FTPServer { int waitForFTPClient(); void processCommand(); -public: - FTPServer(); - virtual ~FTPServer(); - void setCredentials(std::string userid, std::string password); - void start(); - void setPort(uint16_t port); - void setCallbacks(FTPCallbacks* pFTPCallbacks); - static std::string getCurrentDirectory(); - class FileException: public std::exception { - - }; - - // Response codes. - static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; - static const int RESPONSE_200_COMMAND_OK = 200; - static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; - static const int RESPONSE_212_DIRECTORY_STATUS = 212; - static const int RESPONSE_213_FILE_STATUS = 213; - static const int RESPONSE_214_HELP_MESSAGE = 214; - static const int RESPONSE_220_SERVICE_READY = 220; - static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; - static const int RESPONSE_230_USER_LOGGED_IN = 230; - static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; - static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; - static const int RESPONSE_331_PASSWORD_REQUIRED = 331; - static const int RESPONSE_332_NEED_ACCOUNT = 332; - static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; - static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; - static const int RESPONSE_503_BAD_SEQUENCE = 503; - static const int RESPONSE_530_NOT_LOGGED_IN = 530; - static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; - static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; }; - - #endif /* NETWORKING_FTPSERVER_FTPSERVER_H_ */ diff --git a/cpp_utils/File.cpp b/cpp_utils/File.cpp index 670936aa..3b245a85 100644 --- a/cpp_utils/File.cpp +++ b/cpp_utils/File.cpp @@ -33,15 +33,13 @@ File::File(std::string path, uint8_t type) { std::string File::getContent(bool base64Encode) { uint32_t size = length(); ESP_LOGD(LOG_TAG, "File:: getContent(), path=%s, length=%d", m_path.c_str(), size); - if (size == 0) { - return ""; - } - uint8_t *pData = (uint8_t *)malloc(size); + if (size == 0) return ""; + uint8_t* pData = (uint8_t*) malloc(size); if (pData == nullptr) { ESP_LOGE(LOG_TAG, "getContent: Failed to allocate memory"); return ""; } - FILE *file = fopen(m_path.c_str(), "r"); + FILE* file = fopen(m_path.c_str(), "r"); fread(pData, size, 1, file); fclose(file); std::string ret((char *)pData, size); @@ -65,19 +63,17 @@ std::string File::getContent(uint32_t offset, uint32_t readSize) { uint32_t fileSize = length(); ESP_LOGD(LOG_TAG, "File:: getContent(), name=%s, fileSize=%d, offset=%d, readSize=%d", m_path.c_str(), fileSize, offset, readSize); - if (fileSize == 0 || offset > fileSize) { - return ""; - } - uint8_t *pData = (uint8_t *)malloc(readSize); + if (fileSize == 0 || offset > fileSize) return ""; + uint8_t* pData = (uint8_t*) malloc(readSize); if (pData == nullptr) { ESP_LOGE(LOG_TAG, "getContent: Failed to allocate memory"); return ""; } - FILE *file = fopen(m_path.c_str(), "r"); + FILE* file = fopen(m_path.c_str(), "r"); fseek(file, offset, SEEK_SET); size_t bytesRead = fread(pData, 1, readSize, file); fclose(file); - std::string ret((char *)pData, bytesRead); + std::string ret((char*) pData, bytesRead); free(pData); return ret; } // getContent @@ -92,10 +88,8 @@ std::string File::getPath() { */ std::string File::getName() { size_t pos = m_path.find_last_of('/'); - if (pos == std::string::npos) { - return m_path; - } - return m_path.substr(pos+1); + if (pos == std::string::npos) return m_path; + return m_path.substr(pos + 1); } // getName @@ -116,10 +110,8 @@ uint8_t File::getType() { uint32_t File::length() { struct stat buf; int rc = stat(m_path.c_str(), &buf); - if (rc != 0 || S_ISDIR(buf.st_mode)) { - return 0; - } - return buf.st_size; + if (rc != 0 || S_ISDIR(buf.st_mode)) return 0; + return (uint32_t) buf.st_size; } // length @@ -130,8 +122,6 @@ uint32_t File::length() { bool File::isDirectory() { struct stat buf; int rc = stat(m_path.c_str(), &buf); - if (rc != 0) { - return false; - } + if (rc != 0) return false; return S_ISDIR(buf.st_mode); } // isDirectory diff --git a/cpp_utils/File.h b/cpp_utils/File.h index 19ef7c34..c1c18014 100644 --- a/cpp_utils/File.h +++ b/cpp_utils/File.h @@ -17,7 +17,7 @@ class File { public: File(std::string name, uint8_t type = DT_UNKNOWN); - std::string getContent(bool base64Encode=false); + std::string getContent(bool base64Encode = false); std::string getContent(uint32_t offset, uint32_t size); std::string getName(); std::string getPath(); diff --git a/cpp_utils/FileSystem.cpp b/cpp_utils/FileSystem.cpp index 564c68b4..1920235c 100644 --- a/cpp_utils/FileSystem.cpp +++ b/cpp_utils/FileSystem.cpp @@ -26,25 +26,28 @@ static const char* LOG_TAG = "FileSystem"; * @return N/A. */ void FileSystem::dumpDirectory(std::string path) { - DIR *pDir = ::opendir(path.c_str()); + DIR* pDir = ::opendir(path.c_str()); if (pDir == nullptr) { ESP_LOGD(LOG_TAG, "Unable to open directory: %s [errno=%d]", path.c_str(), errno); return; } - struct dirent *pDirent; + struct dirent* pDirent; ESP_LOGD(LOG_TAG, "Directory dump of %s", path.c_str()); - while((pDirent = readdir(pDir)) != nullptr) { + while ((pDirent = readdir(pDir)) != nullptr) { std::string type; - switch(pDirent->d_type) { - case DT_UNKNOWN: - type = "Unknown"; - break; - case DT_REG: - type = "Regular"; - break; - case DT_DIR: - type = "Directory"; - break; + switch (pDirent->d_type) { + case DT_UNKNOWN: + type = "Unknown"; + break; + case DT_REG: + type = "Regular"; + break; + case DT_DIR: + type = "Directory"; + break; + default: + type = "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Entry: d_ino: %d, d_name: %s, d_type: %s", pDirent->d_ino, pDirent->d_name, type.c_str()); } @@ -59,14 +62,14 @@ void FileSystem::dumpDirectory(std::string path) { */ std::vector FileSystem::getDirectoryContents(std::string path) { std::vector ret; - DIR *pDir = ::opendir(path.c_str()); + DIR* pDir = ::opendir(path.c_str()); if (pDir == nullptr) { ESP_LOGE(LOG_TAG, "getDirectoryContents:: Unable to open directory: %s [errno=%d]", path.c_str(), errno); return ret; } - struct dirent *pDirent; + struct dirent* pDirent; ESP_LOGD(LOG_TAG, "Directory dump of %s", path.c_str()); - while((pDirent = readdir(pDir)) != nullptr) { + while ((pDirent = readdir(pDir)) != nullptr) { File file(path +"/" + std::string(pDirent->d_name), pDirent->d_type); ret.push_back(file); } @@ -82,9 +85,7 @@ std::vector FileSystem::getDirectoryContents(std::string path) { bool FileSystem::isDirectory(std::string path) { struct stat statBuf; int rc = stat(path.c_str(), &statBuf); - if (rc != 0) { - return false; - } + if (rc != 0) return false; return S_ISDIR(statBuf.st_mode); } // isDirectory @@ -129,11 +130,11 @@ std::vector FileSystem::pathSplit(std::string path) { std::istringstream stream(path); std::vector ret; std::string pathPart; - while(std::getline(stream, pathPart, '/')) { + while (std::getline(stream, pathPart, '/')) { ret.push_back(pathPart); } // Debug - for (int i=0; i> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); - if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); } else { @@ -134,7 +119,7 @@ void FreeRTOS::Semaphore::give() { xSemaphoreGive(m_semaphore); } // #ifdef ARDUINO_ARCH_ESP32 -// FreeRTOS::sleep(10); +// FreeRTOS::sleep(10); // #endif m_owner = std::string(""); @@ -171,8 +156,7 @@ void FreeRTOS::Semaphore::giveFromISR() { * @param [in] owner The new owner (for debugging) * @return True if we took the semaphore. */ -bool FreeRTOS::Semaphore::take(std::string owner) -{ +bool FreeRTOS::Semaphore::take(std::string owner) { ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); bool rc = false; if (m_usePthreads) { @@ -198,13 +182,12 @@ bool FreeRTOS::Semaphore::take(std::string owner) * @return True if we took the semaphore. */ bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { - ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); bool rc = false; if (m_usePthreads) { assert(false); // We apparently don't have a timed wait for pthreads. } else { - rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS); } m_owner = owner; if (rc) { diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index ab0e83d8..224ed667 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -23,7 +23,7 @@ class FreeRTOS { public: static void sleep(uint32_t ms); - static void startTask(void task(void *), std::string taskName, void *param=nullptr, int stackSize = 2048); + static void startTask(void task(void*), std::string taskName, void* param = nullptr, int stackSize = 2048); static void deleteTask(TaskHandle_t pTask = nullptr); static uint32_t getTimeSinceStart(); @@ -36,10 +36,10 @@ class FreeRTOS { void give(uint32_t value); void giveFromISR(); void setName(std::string name); - bool take(std::string owner=""); - bool take(uint32_t timeoutMs, std::string owner=""); + bool take(std::string owner = ""); + bool take(uint32_t timeoutMs, std::string owner = ""); std::string toString(); - uint32_t wait(std::string owner=""); + uint32_t wait(std::string owner = ""); private: SemaphoreHandle_t m_semaphore; @@ -48,6 +48,7 @@ class FreeRTOS { std::string m_owner; uint32_t m_value; bool m_usePthreads; + }; }; diff --git a/cpp_utils/FreeRTOSTimer.cpp b/cpp_utils/FreeRTOSTimer.cpp index 4baec3e2..4b096000 100644 --- a/cpp_utils/FreeRTOSTimer.cpp +++ b/cpp_utils/FreeRTOSTimer.cpp @@ -11,10 +11,10 @@ #include "FreeRTOSTimer.h" -static std::map timersMap; +static std::map timersMap; void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { - FreeRTOSTimer *timer = timersMap.at(xTimer); + FreeRTOSTimer* timer = timersMap.at(xTimer); timer->callback(timer); } @@ -33,7 +33,7 @@ void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { * * @code{.cpp} * void callback(FreeRTOSTimer *pTimer) { - * // Callback code here ... + * // Callback code here ... * } * @endcode * @@ -43,12 +43,7 @@ void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { * @param [in] data Data to be passed to the callback. * @param [in] callback Callback function to be fired when the timer expires. */ -FreeRTOSTimer::FreeRTOSTimer( - char *name, - TickType_t period, - UBaseType_t reload, - void *data, - void (*callback)(FreeRTOSTimer *pTimer)) { +FreeRTOSTimer::FreeRTOSTimer(char* name, TickType_t period, UBaseType_t reload, void* data, void (*callback)(FreeRTOSTimer* pTimer)) { /* * The callback function to actually be called is saved as member data in the object and * a static callback function is called. This will be passed the FreeRTOS timer handle @@ -124,7 +119,7 @@ void FreeRTOSTimer::changePeriod(TickType_t newPeriod, TickType_t blockTime) { * * @return The name of the timer. */ -const char *FreeRTOSTimer::getName() { +const char* FreeRTOSTimer::getName() { return ::pcTimerGetTimerName(timerHandle); } // getName @@ -134,6 +129,6 @@ const char *FreeRTOSTimer::getName() { * * @return The user supplied data associated with the timer. */ -void *FreeRTOSTimer::getData() { +void* FreeRTOSTimer::getData() { return ::pvTimerGetTimerID(timerHandle); } // getData diff --git a/cpp_utils/FreeRTOSTimer.h b/cpp_utils/FreeRTOSTimer.h index 8556ea3f..5c71a0e7 100644 --- a/cpp_utils/FreeRTOSTimer.h +++ b/cpp_utils/FreeRTOSTimer.h @@ -15,21 +15,22 @@ */ class FreeRTOSTimer { public: - FreeRTOSTimer(char *name, TickType_t period, UBaseType_t reload, void *data, void (*callback)(FreeRTOSTimer *pTimer)); + FreeRTOSTimer(char* name, TickType_t period, UBaseType_t reload, void* data, void (*callback)(FreeRTOSTimer* pTimer)); virtual ~FreeRTOSTimer(); - void changePeriod(TickType_t newPeriod, TickType_t blockTime=portMAX_DELAY); - void *getData(); - const char *getName(); + void changePeriod(TickType_t newPeriod, TickType_t blockTime = portMAX_DELAY); + void* getData(); + const char* getName(); TickType_t getPeriod(); - void reset(TickType_t blockTime=portMAX_DELAY); - void start(TickType_t blockTime=portMAX_DELAY); - void stop(TickType_t blockTime=portMAX_DELAY); + void reset(TickType_t blockTime = portMAX_DELAY); + void start(TickType_t blockTime = portMAX_DELAY); + void stop(TickType_t blockTime = portMAX_DELAY); private: TimerHandle_t timerHandle; TickType_t period; - void (*callback)(FreeRTOSTimer *pTimer); + void (*callback)(FreeRTOSTimer* pTimer); static void internalCallback(TimerHandle_t xTimer); + }; #endif /* COMPONENTS_CPP_UTILS_FREERTOSTIMER_H_ */ diff --git a/cpp_utils/GPIO.cpp b/cpp_utils/GPIO.cpp index 23cf6fb7..bc3409cd 100644 --- a/cpp_utils/GPIO.cpp +++ b/cpp_utils/GPIO.cpp @@ -22,15 +22,11 @@ static bool g_isrServiceInstalled = false; * @param [in] handler The function to be invoked when the interrupt is detected. * @param [in] pArgs Optional arguments to pass to the handler. */ -void ESP32CPP::GPIO::addISRHandler( - gpio_num_t pin, - gpio_isr_t handler, - void* pArgs) { - +void ESP32CPP::GPIO::addISRHandler(gpio_num_t pin, gpio_isr_t handler, void* pArgs) { ESP_LOGD(LOG_TAG, ">> addISRHandler: pin=%d", pin); // If we have not yet installed the ISR service handler, install it now. - if (g_isrServiceInstalled == false) { + if (!g_isrServiceInstalled) { ESP_LOGD(LOG_TAG, "Installing the global ISR service"); esp_err_t errRc = ::gpio_install_isr_service(0); if (errRc != ESP_OK) { @@ -70,10 +66,7 @@ void ESP32CPP::GPIO::high(gpio_num_t pin) { * @return The value of true if the pin is valid and false otherwise. */ bool ESP32CPP::GPIO::inRange(gpio_num_t pin) { - if (pin>=0 && pin<=39) { - return true; - } - return false; + return (pin >= 0 && pin <= 39); } // inRange @@ -155,14 +148,11 @@ void ESP32CPP::GPIO::setInput(gpio_num_t pin) { * @param [in] intrType The type of interrupt. * @return N/A. */ -void ESP32CPP::GPIO::setInterruptType( - gpio_num_t pin, - gpio_int_type_t intrType) { +void ESP32CPP::GPIO::setInterruptType(gpio_num_t pin, gpio_int_type_t intrType) { esp_err_t rc = ::gpio_set_intr_type(pin, intrType); if (rc != ESP_OK) { ESP_LOGE(LOG_TAG, "setInterruptType: %d", rc); } - } // setInterruptType @@ -204,9 +194,9 @@ void ESP32CPP::GPIO::write(gpio_num_t pin, bool value) { */ void ESP32CPP::GPIO::writeByte(gpio_num_t pins[], uint8_t value, int bits) { ESP_LOGD(LOG_TAG, ">> writeByte: value: %.2x, bits: %d", value, bits); - for (int i=0; i -namespace ESP32CPP -{ +namespace ESP32CPP { /** * @brief Interface to %GPIO functions. * diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 2c0f0846..80c5cc6e 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -23,20 +23,20 @@ static const char* LOG_TAG = "GeneralUtils"; static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; static int base64EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; } // base64EncodedLength -static int base64EncodedLength(const std::string &in) { +static int base64EncodedLength(const std::string& in) { return base64EncodedLength(in.length()); } // base64EncodedLength -static void a3_to_a4(unsigned char * a4, unsigned char * a3) { +static void a3_to_a4(unsigned char* a4, unsigned char* a3) { a4[0] = (a3[0] & 0xfc) >> 2; a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); @@ -44,7 +44,7 @@ static void a3_to_a4(unsigned char * a4, unsigned char * a3) { } // a3_to_a4 -static void a4_to_a3(unsigned char * a3, unsigned char * a4) { +static void a4_to_a3(unsigned char* a3, unsigned char* a4) { a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; @@ -56,7 +56,7 @@ static void a4_to_a3(unsigned char * a3, unsigned char * a4) { * @param [in] in * @param [out] out */ -bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { +bool GeneralUtils::base64Encode(const std::string& in, std::string* out) { int i = 0, j = 0; size_t enc_len = 0; unsigned char a3[3]; @@ -127,16 +127,16 @@ bool GeneralUtils::endsWith(std::string str, char c) { if (str.empty()) { return false; } - if (str.at(str.length()-1) == c) { + if (str.at(str.length() - 1) == c) { return true; } return false; } // endsWidth -static int DecodedLength(const std::string &in) { +static int DecodedLength(const std::string& in) { int numEq = 0; - int n = in.size(); + int n = (int) in.size(); for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; @@ -160,7 +160,7 @@ static unsigned char b64_lookup(unsigned char c) { * @param [in] in The string to be decoded. * @param [out] out The resulting data. */ -bool GeneralUtils::base64Decode(const std::string &in, std::string *out) { +bool GeneralUtils::base64Decode(const std::string& in, std::string* out) { int i = 0, j = 0; size_t dec_len = 0; unsigned char a3[3]; @@ -300,8 +300,8 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { ESP_LOGV(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); strcpy(ascii, ""); strcpy(hex, ""); - uint32_t index=0; - while(index < length) { + uint32_t index = 0; + while (index < length) { sprintf(tempBuf, "%.2x ", pData[index]); strcat(hex, tempBuf); if (isprint(pData[index])) { @@ -312,18 +312,18 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { strcat(ascii, tempBuf); index++; if (index % 16 == 0) { - ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); strcpy(ascii, ""); strcpy(hex, ""); lineNumber++; } } if (index %16 != 0) { - while(index % 16 != 0) { + while (index % 16 != 0) { strcat(hex, " "); index++; } - ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); } } // hexDump @@ -335,7 +335,7 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { */ std::string GeneralUtils::ipToString(uint8_t *ip) { std::stringstream s; - s << (int)ip[0] << '.' << (int)ip[1] << '.' << (int)ip[2] << '.' << (int)ip[3]; + s << (int) ip[0] << '.' << (int) ip[1] << '.' << (int) ip[2] << '.' << (int) ip[3]; return s.str(); } // ipToString @@ -351,7 +351,7 @@ std::vector GeneralUtils::split(std::string source, char delimiter) std::vector strings; std::istringstream iss(source); std::string s; - while(std::getline(iss, s, delimiter)) { + while (std::getline(iss, s, delimiter)) { strings.push_back(trim(s)); } return strings; @@ -364,7 +364,7 @@ std::vector GeneralUtils::split(std::string source, char delimiter) * @return A string representation of the error code. */ const char* GeneralUtils::errorToString(esp_err_t errCode) { - switch(errCode) { + switch (errCode) { case ESP_OK: return "ESP_OK"; case ESP_FAIL: @@ -431,8 +431,9 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "ESP_ERR_WIFI_TIMEOUT"; case ESP_ERR_WIFI_WAKE_FAIL: return "ESP_ERR_WIFI_WAKE_FAIL"; + default: + return "Unknown ESP_ERR error"; } - return "Unknown ESP_ERR error"; } // errorToString /** @@ -443,75 +444,72 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { * @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h. */ const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { - if (errCode == ESP_OK) - return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)"; - if (errCode == UINT8_MAX) - return "Not Connected (default value)"; - - switch((wifi_err_reason_t) errCode) { - case WIFI_REASON_UNSPECIFIED: - return "WIFI_REASON_UNSPECIFIED"; - case WIFI_REASON_AUTH_EXPIRE: - return "WIFI_REASON_AUTH_EXPIRE"; - case WIFI_REASON_AUTH_LEAVE: - return "WIFI_REASON_AUTH_LEAVE"; - case WIFI_REASON_ASSOC_EXPIRE: - return "WIFI_REASON_ASSOC_EXPIRE"; - case WIFI_REASON_ASSOC_TOOMANY: - return "WIFI_REASON_ASSOC_TOOMANY"; - case WIFI_REASON_NOT_AUTHED: - return "WIFI_REASON_NOT_AUTHED"; - case WIFI_REASON_NOT_ASSOCED: - return "WIFI_REASON_NOT_ASSOCED"; - case WIFI_REASON_ASSOC_LEAVE: - return "WIFI_REASON_ASSOC_LEAVE"; - case WIFI_REASON_ASSOC_NOT_AUTHED: - return "WIFI_REASON_ASSOC_NOT_AUTHED"; - case WIFI_REASON_DISASSOC_PWRCAP_BAD: - return "WIFI_REASON_DISASSOC_PWRCAP_BAD"; - case WIFI_REASON_DISASSOC_SUPCHAN_BAD: - return "WIFI_REASON_DISASSOC_SUPCHAN_BAD"; - case WIFI_REASON_IE_INVALID: - return "WIFI_REASON_IE_INVALID"; - case WIFI_REASON_MIC_FAILURE: - return "WIFI_REASON_MIC_FAILURE"; - case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: - return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT"; - case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: - return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT"; - case WIFI_REASON_IE_IN_4WAY_DIFFERS: - return "WIFI_REASON_IE_IN_4WAY_DIFFERS"; - case WIFI_REASON_GROUP_CIPHER_INVALID: - return "WIFI_REASON_GROUP_CIPHER_INVALID"; - case WIFI_REASON_PAIRWISE_CIPHER_INVALID: - return "WIFI_REASON_PAIRWISE_CIPHER_INVALID"; - case WIFI_REASON_AKMP_INVALID: - return "WIFI_REASON_AKMP_INVALID"; - case WIFI_REASON_UNSUPP_RSN_IE_VERSION: - return "WIFI_REASON_UNSUPP_RSN_IE_VERSION"; - case WIFI_REASON_INVALID_RSN_IE_CAP: - return "WIFI_REASON_INVALID_RSN_IE_CAP"; - case WIFI_REASON_802_1X_AUTH_FAILED: - return "WIFI_REASON_802_1X_AUTH_FAILED"; - case WIFI_REASON_CIPHER_SUITE_REJECTED: - return "WIFI_REASON_CIPHER_SUITE_REJECTED"; - - case WIFI_REASON_BEACON_TIMEOUT: - return "WIFI_REASON_BEACON_TIMEOUT"; - case WIFI_REASON_NO_AP_FOUND: - return "WIFI_REASON_NO_AP_FOUND"; - case WIFI_REASON_AUTH_FAIL: - return "WIFI_REASON_AUTH_FAIL"; - case WIFI_REASON_ASSOC_FAIL: - return "WIFI_REASON_ASSOC_FAIL"; - case WIFI_REASON_HANDSHAKE_TIMEOUT: - return "WIFI_REASON_HANDSHAKE_TIMEOUT"; + if (errCode == ESP_OK) return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)"; + if (errCode == UINT8_MAX) return "Not Connected (default value)"; + + switch ((wifi_err_reason_t) errCode) { + case WIFI_REASON_UNSPECIFIED: + return "WIFI_REASON_UNSPECIFIED"; + case WIFI_REASON_AUTH_EXPIRE: + return "WIFI_REASON_AUTH_EXPIRE"; + case WIFI_REASON_AUTH_LEAVE: + return "WIFI_REASON_AUTH_LEAVE"; + case WIFI_REASON_ASSOC_EXPIRE: + return "WIFI_REASON_ASSOC_EXPIRE"; + case WIFI_REASON_ASSOC_TOOMANY: + return "WIFI_REASON_ASSOC_TOOMANY"; + case WIFI_REASON_NOT_AUTHED: + return "WIFI_REASON_NOT_AUTHED"; + case WIFI_REASON_NOT_ASSOCED: + return "WIFI_REASON_NOT_ASSOCED"; + case WIFI_REASON_ASSOC_LEAVE: + return "WIFI_REASON_ASSOC_LEAVE"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "WIFI_REASON_ASSOC_NOT_AUTHED"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "WIFI_REASON_DISASSOC_PWRCAP_BAD"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "WIFI_REASON_DISASSOC_SUPCHAN_BAD"; + case WIFI_REASON_IE_INVALID: + return "WIFI_REASON_IE_INVALID"; + case WIFI_REASON_MIC_FAILURE: + return "WIFI_REASON_MIC_FAILURE"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "WIFI_REASON_IE_IN_4WAY_DIFFERS"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "WIFI_REASON_GROUP_CIPHER_INVALID"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "WIFI_REASON_PAIRWISE_CIPHER_INVALID"; + case WIFI_REASON_AKMP_INVALID: + return "WIFI_REASON_AKMP_INVALID"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "WIFI_REASON_UNSUPP_RSN_IE_VERSION"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "WIFI_REASON_INVALID_RSN_IE_CAP"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "WIFI_REASON_802_1X_AUTH_FAILED"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "WIFI_REASON_CIPHER_SUITE_REJECTED"; + case WIFI_REASON_BEACON_TIMEOUT: + return "WIFI_REASON_BEACON_TIMEOUT"; + case WIFI_REASON_NO_AP_FOUND: + return "WIFI_REASON_NO_AP_FOUND"; + case WIFI_REASON_AUTH_FAIL: + return "WIFI_REASON_AUTH_FAIL"; + case WIFI_REASON_ASSOC_FAIL: + return "WIFI_REASON_ASSOC_FAIL"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_HANDSHAKE_TIMEOUT"; + default: + return "Unknown ESP_ERR error"; } - return "Unknown ESP_ERR error"; } // wifiErrorToString - /** * @brief Convert a string to lower case. * @param [in] value The string to convert to lower case. @@ -528,15 +526,9 @@ std::string GeneralUtils::toLower(std::string& value) { /** * @brief Remove white space from a string. */ -std::string GeneralUtils::trim(const std::string& str) -{ - size_t first = str.find_first_not_of(' '); - if (std::string::npos == first) - { - return str; - } - size_t last = str.find_last_not_of(' '); - return str.substr(first, (last - first + 1)); +std::string GeneralUtils::trim(const std::string& str) { + size_t first = str.find_first_not_of(' '); + if (std::string::npos == first) return str; + size_t last = str.find_last_not_of(' '); + return str.substr(first, (last - first + 1)); } // trim - - diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 3706040e..8eecbd4d 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -23,7 +23,7 @@ class GeneralUtils { static void dumpInfo(); static bool endsWith(std::string str, char c); static const char* errorToString(esp_err_t errCode); - static const char* wifiErrorToString(uint8_t value); + static const char* wifiErrorToString(uint8_t value); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); static std::vector split(std::string source, char delimiter); diff --git a/cpp_utils/HIDKeyboardTypes.h b/cpp_utils/HIDKeyboardTypes.h index ef48a526..4e221d57 100644 --- a/cpp_utils/HIDKeyboardTypes.h +++ b/cpp_utils/HIDKeyboardTypes.h @@ -26,9 +26,9 @@ /* Modifiers */ enum MODIFIER_KEY { - KEY_CTRL = 1, - KEY_SHIFT = 2, - KEY_ALT = 4, + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, }; @@ -43,37 +43,37 @@ enum MEDIA_KEY { }; enum FUNCTION_KEY { - KEY_F1 = 128, /* F1 key */ - KEY_F2, /* F2 key */ - KEY_F3, /* F3 key */ - KEY_F4, /* F4 key */ - KEY_F5, /* F5 key */ - KEY_F6, /* F6 key */ - KEY_F7, /* F7 key */ - KEY_F8, /* F8 key */ - KEY_F9, /* F9 key */ - KEY_F10, /* F10 key */ - KEY_F11, /* F11 key */ - KEY_F12, /* F12 key */ + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ - KEY_PRINT_SCREEN, /* Print Screen key */ - KEY_SCROLL_LOCK, /* Scroll lock */ - KEY_CAPS_LOCK, /* caps lock */ - KEY_NUM_LOCK, /* num lock */ - KEY_INSERT, /* Insert key */ - KEY_HOME, /* Home key */ - KEY_PAGE_UP, /* Page Up key */ - KEY_PAGE_DOWN, /* Page Down key */ + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ - RIGHT_ARROW, /* Right arrow */ - LEFT_ARROW, /* Left arrow */ - DOWN_ARROW, /* Down arrow */ - UP_ARROW, /* Up arrow */ + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ }; typedef struct { - unsigned char usage; - unsigned char modifier; + unsigned char usage; + unsigned char modifier; } KEYMAP; #ifdef US_KEYBOARD diff --git a/cpp_utils/HIDTypes.h b/cpp_utils/HIDTypes.h index 726b84be..64850ef8 100644 --- a/cpp_utils/HIDTypes.h +++ b/cpp_utils/HIDTypes.h @@ -89,8 +89,8 @@ #define MAX_HID_REPORT_SIZE (64) typedef struct { - uint32_t length; - uint8_t data[MAX_HID_REPORT_SIZE]; + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; } HID_REPORT; #endif diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index d9040659..a6bbbc34 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -42,11 +42,11 @@ static std::string lineTerminator = "\r\n"; * @param [in] str The string we are parsing. * @param [in] token The token delimiter. */ -static std::string toStringToken(std::string::iterator &it, std::string &str, std::string &token) { +static std::string toStringToken(std::string::iterator& it, std::string& str, std::string& token) { std::string ret; std::string part; auto itToken = token.begin(); - for(; it != str.end(); ++it) { + for (; it != str.end(); ++it) { if ((*it) == (*itToken)) { part += (*itToken); ++itToken; @@ -74,7 +74,7 @@ static std::string toStringToken(std::string::iterator &it, std::string &str, st * @param [in] token The token terminating the parse. * @return The parsed string token. */ -static std::string toCharToken(std::string::iterator &it, std::string &str, char token) { +static std::string toCharToken(std::string::iterator& it, std::string& str, char token) { std::string ret; for(; it != str.end(); ++it) { if ((*it) == token) { @@ -97,9 +97,9 @@ static std::string toCharToken(std::string::iterator &it, std::string &str, char * @param [in] line The line of text to parse. * @return A pair of the form name/value. */ -std::pair parseHeader(std::string &line) { - auto it = line.begin(); - std::string name = toCharToken(it, line, ':'); // Parse the line until we find a ':' +std::pair parseHeader(std::string& line) { + auto it = line.begin(); + std::string name = toCharToken(it, line, ':'); // Parse the line until we find a ':' // We normalize the header name to be lower case. GeneralUtils::toLower(name); auto value = GeneralUtils::trim(toStringToken(it, line, lineTerminator)); @@ -142,9 +142,7 @@ std::string HttpParser::getHeader(const std::string& name) { // We normalize the header name to be lower case. std::string localName = name; GeneralUtils::toLower(localName); - if (!hasHeader(localName)) { - return ""; - } + if (!hasHeader(localName)) return ""; return m_headers.at(localName); } // getHeader @@ -169,11 +167,11 @@ std::string HttpParser::getVersion() { } // getVersion std::string HttpParser::getStatus() { - return m_status; + return m_status; } // getStatus std::string HttpParser::getReason() { - return m_reason; + return m_reason; } // getReason /** @@ -198,7 +196,7 @@ void HttpParser::parse(Socket s) { line = s.readToDelim(lineTerminator); parseRequestLine(line); line = s.readToDelim(lineTerminator); - while(!line.empty()) { + while (!line.empty()) { m_headers.insert(parseHeader(line)); line = s.readToDelim(lineTerminator); } @@ -216,13 +214,13 @@ void HttpParser::parse(Socket s) { int length = std::atoi(val.c_str()); uint8_t data[length]; s.receive(data, length, true); - m_body = std::string((char *)data, length); + m_body = std::string((char*) data, length); } else { uint8_t data[512]; int rc = s.receive(data, sizeof(data)); - if (rc > 0) { - m_body = std::string((char *)data, rc); - } + if (rc > 0) { + m_body = std::string((char*) data, rc); + } } ESP_LOGD(LOG_TAG, "<< parse: Size of body: %d", m_body.length()); } // parse @@ -253,10 +251,9 @@ void HttpParser::parse(std::string message) { * @brief Parse A request line. * @param [in] line The request line to parse. */ -// A request Line is built from: -// -// -void HttpParser::parseRequestLine(std::string &line) { +void HttpParser::parseRequestLine(std::string& line) { + // A request Line is built from: + // ESP_LOGD(LOG_TAG, ">> parseRequestLine: \"%s\" [%d]", line.c_str(), line.length()); std::string::iterator it = line.begin(); @@ -275,17 +272,15 @@ void HttpParser::parseRequestLine(std::string &line) { * @brief Parse a response message. * @param [in] line The response to parse. */ -// A response is built from: -// A status line, any number of header lines, a body -// -void HttpParser::parseResponse(std::string message) -{ +void HttpParser::parseResponse(std::string message) { + // A response is built from: + // A status line, any number of header lines, a body auto it = message.begin(); auto line = toStringToken(it, message, lineTerminator); parseStatusLine(line); line = toStringToken(it, message, lineTerminator); - while(!line.empty()) { + while (!line.empty()) { ESP_LOGD(LOG_TAG, "Header: \"%s\"", line.c_str()); m_headers.insert(parseHeader(line)); line = toStringToken(it, message, lineTerminator); @@ -298,11 +293,9 @@ void HttpParser::parseResponse(std::string message) * @brief Parse A status line. * @param [in] line The status line to parse. */ -// A status Line is built from: -// -// -void HttpParser::parseStatusLine(std::string &line) -{ +void HttpParser::parseStatusLine(std::string& line) { + // A status Line is built from: + // ESP_LOGD(LOG_TAG, ">> ParseStatusLine: \"%s\" [%d]", line.c_str(), line.length()); std::string::iterator it = line.begin(); // Get the version @@ -314,4 +307,3 @@ void HttpParser::parseStatusLine(std::string &line) ESP_LOGD(LOG_TAG, "<< ParseStatusLine: method: %s, version: %s, status: %s", m_method.c_str(), m_version.c_str(), m_status.c_str()); } // parseRequestLine - diff --git a/cpp_utils/HttpParser.h b/cpp_utils/HttpParser.h index 47aafcea..06485293 100644 --- a/cpp_utils/HttpParser.h +++ b/cpp_utils/HttpParser.h @@ -12,17 +12,6 @@ #include "Socket.h" class HttpParser { -private: - std::string m_method; - std::string m_url; - std::string m_version; - std::string m_body; - std::string m_status; - std::string m_reason; - std::map m_headers; - void dump(); - void parseRequestLine(std::string &line); - void parseStatusLine(std::string &line); public: HttpParser(); virtual ~HttpParser(); @@ -32,12 +21,25 @@ class HttpParser { std::string getMethod(); std::string getURL(); std::string getVersion(); - std::string getStatus(); - std::string getReason(); + std::string getStatus(); + std::string getReason(); bool hasHeader(const std::string& name); void parse(std::string message); void parse(Socket s); - void parseResponse(std::string message); + void parseResponse(std::string message); + +private: + std::string m_method; + std::string m_url; + std::string m_version; + std::string m_body; + std::string m_status; + std::string m_reason; + std::map m_headers; + void dump(); + void parseRequestLine(std::string& line); + void parseStatusLine(std::string& line); + }; #endif /* CPP_UTILS_HTTPPARSER_H_ */ diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index 9261af60..ff9336fb 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -41,6 +41,8 @@ #include #include +#define STATE_NAME 0 +#define STATE_VALUE 1 static const char* LOG_TAG="HttpRequest"; @@ -79,10 +81,10 @@ const char HttpRequest::HTTP_METHOD_PUT[] = "PUT"; std::string buildWebsocketKeyResponseHash(std::string requestKey) { std::string newKey = requestKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; uint8_t shaData[20]; - esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData); + esp_sha(SHA1, (uint8_t*) newKey.data(), newKey.length(), shaData); //GeneralUtils::hexDump(shaData, 20); std::string retStr; - GeneralUtils::base64Encode(std::string((char*)shaData, sizeof(shaData)), &retStr); + GeneralUtils::base64Encode(std::string((char*) shaData, sizeof(shaData)), &retStr); return retStr; } // buildWebsocketKeyResponseHash @@ -104,9 +106,8 @@ HttpRequest::HttpRequest(Socket clientSocket) { // a delimiter and then examine each of the parts to see if any of those are "Upgrade". std::vector parts = GeneralUtils::split(getHeader(HTTP_HEADER_CONNECTION), ','); bool upgradeFound = false; - if (std::find(parts.begin(), parts.end(), "Upgrade") != parts.end()) - { - upgradeFound = true; + if (std::find(parts.begin(), parts.end(), "Upgrade") != parts.end()) { + upgradeFound = true; } // Is this a Web Socket? @@ -114,7 +115,7 @@ HttpRequest::HttpRequest(Socket clientSocket) { !getHeader(HTTP_HEADER_HOST).empty() && getHeader(HTTP_HEADER_UPGRADE) == "websocket" && //getHeader(HTTP_HEADER_CONNECTION) == "Upgrade" && - upgradeFound == true && + upgradeFound && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_KEY).empty() && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_VERSION).empty()) { ESP_LOGD(LOG_TAG, "Websocket detected!"); @@ -153,9 +154,6 @@ void HttpRequest::close() { } // close_cpp - - - /** * @brief Dump the HttpRequest for debugging purposes. */ @@ -203,9 +201,6 @@ std::string HttpRequest::getPath() { } // getPath -#define STATE_NAME 0 -#define STATE_VALUE 1 - /** * @brief Get the query part of the request. * The query is a set of name = value pairs. The return is a map keyed by the name items. @@ -218,9 +213,9 @@ std::map HttpRequest::getQuery() { std::map queryMap; std::string possibleQueryString = getPath(); - int qindex = possibleQueryString.find_first_of("?") ; + int qindex = possibleQueryString.find_first_of("?"); if (qindex < 0) { - ESP_LOGD(LOG_TAG, "No query string present") ; + ESP_LOGD(LOG_TAG, "No query string present"); return queryMap ; } std::string queryString = possibleQueryString.substr(qindex + 1, -1) ; @@ -235,7 +230,7 @@ std::map HttpRequest::getQuery() { std::string name = ""; std::string value; // Loop through each character in the query string. - for (int i=0; i HttpRequest::getQuery() { } // getQuery - /** * @brief Get the underlying socket. * @return The underlying socket. @@ -353,11 +347,11 @@ std::vector HttpRequest::pathSplit() { std::istringstream stream(getPath()); std::vector ret; std::string pathPart; - while(std::getline(stream, pathPart, '/')) { + while (std::getline(stream, pathPart, '/')) { ret.push_back(pathPart); } // Debug - for (int i=0; i parseForm(); // Parse the body as a form. std::vector pathSplit(); std::string urlDecode(std::string str); // Decode a URL. +private: + Socket m_clientSocket; // The socket connected to the client. + bool m_isClosed; // Is the client connection closed? + HttpParser m_parser; // The parse to parse HTTP data. + WebSocket* m_pWebSocket; // A possible reference to a WebSocket object instance. + }; #endif /* COMPONENTS_CPP_UTILS_HTTPREQUEST_H_ */ diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index 4c5c1a4e..c6e9683c 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -26,12 +26,13 @@ const int HttpResponse::HTTP_STATUS_NOT_IMPLEMENTED = 501; const int HttpResponse::HTTP_STATUS_SERVICE_UNAVAILABLE = 503; static std::string lineTerminator = "\r\n"; -HttpResponse::HttpResponse(HttpRequest *request) { m_request = request; m_status = 200; +HttpResponse::HttpResponse(HttpRequest* request) { m_headerCommitted = false; // We have not yet sent a header. } + HttpResponse::~HttpResponse() { } @@ -43,9 +44,7 @@ HttpResponse::~HttpResponse() { * @param [in] value The value of the header. */ void HttpResponse::addHeader(const std::string name, const std::string value) { - if (m_headerCommitted) { - return; - } + if (m_headerCommitted) return; m_responseHeaders.insert(std::pair(name, value)); } // addHeader @@ -57,7 +56,7 @@ void HttpResponse::addHeader(const std::string name, const std::string value) { */ void HttpResponse::close() { // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { sendHeader(); } m_request->close(); @@ -70,9 +69,7 @@ void HttpResponse::close() { * @return The value of the named header. */ std::string HttpResponse::getHeader(std::string name) { - if (m_responseHeaders.find(name) == m_responseHeaders.end()) { - return ""; - } + if (m_responseHeaders.find(name) == m_responseHeaders.end()) return ""; return m_responseHeaders.at(name); } // getHeader @@ -97,7 +94,7 @@ void HttpResponse::sendData(std::string data) { } // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { sendHeader(); } @@ -115,7 +112,7 @@ void HttpResponse::sendData(uint8_t* pData, size_t size) { } // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { sendHeader(); } @@ -124,8 +121,7 @@ void HttpResponse::sendData(uint8_t* pData, size_t size) { ESP_LOGD(LOG_TAG, "<< sendData"); } // sendData -void HttpResponse::sendFile(std::string fileName, size_t bufSize) -{ +void HttpResponse::sendFile(std::string fileName, size_t bufSize) { ESP_LOGI(LOG_TAG, "Opening file: %s", fileName.c_str()); std::ifstream ifStream; ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. @@ -139,15 +135,15 @@ void HttpResponse::sendFile(std::string fileName, size_t bufSize) close(); return; // Since we failed to open the file, no further work to be done. } - + // We now have an open file and want to push the content of that file through to the browser. // because of defect #252 we have to do some pretty important re-work here. Specifically, we can't host the whole file in // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. - + setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); uint8_t *pData = new uint8_t[bufSize]; - while(!ifStream.eof()) { - ifStream.read((char *)pData, bufSize); + while (!ifStream.eof()) { + ifStream.read((char*) pData, bufSize); sendData(pData, ifStream.gcount()); } delete[] pData; @@ -160,7 +156,7 @@ void HttpResponse::sendFile(std::string fileName, size_t bufSize) */ void HttpResponse::sendHeader() { // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { std::ostringstream oss; oss << m_request->getVersion() << " " << m_status << " " << m_statusMessage << lineTerminator; for (auto it = m_responseHeaders.begin(); it != m_responseHeaders.end(); ++it) { @@ -188,5 +184,3 @@ void HttpResponse::setStatus(const int status, const std::string message) { m_status = status; m_statusMessage = message; } // setStatus - - diff --git a/cpp_utils/HttpResponse.h b/cpp_utils/HttpResponse.h index 7080ea90..cc7be388 100644 --- a/cpp_utils/HttpResponse.h +++ b/cpp_utils/HttpResponse.h @@ -14,15 +14,6 @@ #include "HttpRequest.h" class HttpResponse { -private: - bool m_headerCommitted; // Has the header been sent? - HttpRequest* m_request; // The request associated with this response. - std::map m_responseHeaders; // The headers to be sent with the response. - int m_status; // The status to be sent with the response. - std::string m_statusMessage; // The status message to be sent with the response. - - void sendHeader(); // Send the header to the client. - public: static const int HTTP_STATUS_CONTINUE; static const int HTTP_STATUS_SWITCHING_PROTOCOL; @@ -46,8 +37,17 @@ class HttpResponse { std::map getHeaders(); // Get all headers. void sendData(std::string data); // Send data to the client. void sendData(uint8_t* pData, size_t size); // Send data to the client. - void sendFile(std::string fileName, size_t bufSize=4*1024); // Send file contents if exists. void setStatus(int status, std::string message); // Set the response status. + void sendFile(std::string fileName, size_t bufSize = 4 * 1024); // Send file contents if exists. +private: + bool m_headerCommitted; // Has the header been sent? + HttpRequest* m_request; // The request associated with this response. + std::map m_responseHeaders; // The headers to be sent with the response. + int m_status; // The status to be sent with the response. + std::string m_statusMessage; // The status message to be sent with the response. + + void sendHeader(); // Send the header to the client. + }; #endif /* COMPONENTS_CPP_UTILS_HTTPRESPONSE_H_ */ diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 1b1845f1..fb90fcc9 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -26,17 +26,16 @@ static const char* LOG_TAG = "HttpServer"; #undef close - /** * Constructor for HTTP Server */ HttpServer::HttpServer() { - m_fileBufferSize = 4*1024; // Default size of the file buffer. m_portNumber = 80; // The default port number. m_clientTimeout = 5; // The default timeout 5 seconds. m_rootPath = ""; // The default path. m_useSSL = false; // Default SSL is no. setDirectoryListing(false); // Default directory listing is disabled. + m_fileBufferSize = 4 * 1024; // Default size of the file buffer. } // HttpServer @@ -44,6 +43,7 @@ HttpServer::~HttpServer() { ESP_LOGD(LOG_TAG, "~HttpServer"); } + /** * @brief Be an HTTP server task. * Here we define a Task that will be run when the HTTP server starts. It is this task @@ -52,7 +52,7 @@ HttpServer::~HttpServer() { */ class HttpServerTask: public Task { public: - HttpServerTask(std::string name): Task(name, 16*1024) { + HttpServerTask(std::string name): Task(name, 16 * 1024) { m_pHttpServer = nullptr; }; @@ -70,7 +70,7 @@ class HttpServerTask: public Task { * * @param [in] request The HTTP request to process. */ - void processRequest(HttpRequest &request) { + void processRequest(HttpRequest& request) { ESP_LOGD("HttpServerTask", ">> processRequest: Method: %s, Path: %s", request.getMethod().c_str(), request.getPath().c_str()); @@ -107,9 +107,9 @@ class HttpServerTask: public Task { // If the file name ends with a '/' then remove it ... we are normalizing to NO trailing slashes. if (GeneralUtils::endsWith(fileName, '/')) { - fileName = fileName.substr(0, fileName.length()-1); + fileName = fileName.substr(0, fileName.length() - 1); } - + HttpResponse response(&request); // Test if the path is a directory. if (FileSystem::isDirectory(fileName)) { @@ -129,20 +129,18 @@ class HttpServerTask: public Task { * @param [in] data A reference to the HttpServer. */ void run(void* data) { - m_pHttpServer = (HttpServer*)data; // The passed in data is an instance of an HttpServer. + m_pHttpServer = (HttpServer*) data; // The passed in data is an instance of an HttpServer. m_pHttpServer->m_socket.setSSL(m_pHttpServer->m_useSSL); m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber, false /* is datagram */, true /* Allow address reuse */); ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort()); Socket clientSocket; - while(1) { // Loop forever. - + while (true) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); try { clientSocket = m_pHttpServer->m_socket.accept(); // Block waiting for a new external client connection. clientSocket.setTimeout(m_pHttpServer->getClientTimeout()); - } - catch(std::exception &e) { + } catch (std::exception& e) { ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); m_pHttpServer->m_semaphoreServerStarted.give(); // Release the semaphore .. we are now no longer running. return; @@ -174,7 +172,7 @@ class HttpServerTask: public Task { * Example: * @code{.cpp} * static void handle_REST_WiFi(WebServer::HttpRequest *pRequest, WebServer::HttpResponse *pResponse) { - * ... + * ... * } * * webServer.addPathHandler("GET", "\\/ESP32\\/WiFi", handle_REST_WiFi); @@ -187,7 +185,7 @@ class HttpServerTask: public Task { void HttpServer::addPathHandler( std::string method, std::regex* pathExpr, - void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { + void (*handler)(HttpRequest* pHttpRequest, HttpResponse* pHttpResponse)) { // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); @@ -204,7 +202,7 @@ void HttpServer::addPathHandler( * Example: * @code{.cpp} * static void handle_REST_WiFi(WebServer::HttpRequest *pRequest, WebServer::HttpResponse *pResponse) { - * ... + * ... * } * * webServer.addPathHandler("GET", "/ESP32/WiFi", handle_REST_WiFi); @@ -217,7 +215,7 @@ void HttpServer::addPathHandler( void HttpServer::addPathHandler( std::string method, std::string path, - void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { + void (*handler)(HttpRequest* pHttpRequest, HttpResponse* pHttpResponse)) { // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. m_pathHandlers.push_back(PathHandler(method, path, handler)); @@ -299,8 +297,7 @@ void HttpServer::listDirectory(std::string path, HttpResponse& response) { ss << "" << it->getName() << ""; if (it->isDirectory()) { ss << "<dir>"; - } - else { + } else { ss << "" << it->length() << ""; } @@ -314,6 +311,7 @@ void HttpServer::listDirectory(std::string path, HttpResponse& response) { response.close(); } // listDirectory + /** * @brief Set different socket timeout for new connections. * @param [in] use Set to true to enable directory listing. @@ -322,6 +320,7 @@ void HttpServer::setClientTimeout(uint32_t timeout) { m_clientTimeout = timeout; } + /** * @brief Get current socket's timeout for new connections. * @param [in] use Set to true to enable directory listing. @@ -330,6 +329,7 @@ uint32_t HttpServer::getClientTimeout() { return m_clientTimeout; } + /** * @brief Set whether or not we will list directories. * @param [in] use Set to true to enable directory listing. @@ -369,7 +369,6 @@ void HttpServer::setFileBufferSize(size_t fileBufferSize) { * @return N/A. */ void HttpServer::setRootPath(std::string path) { - // Should the user have supplied a path that ends in a "/" remove the trailing slash. This also // means that "/" becomes "". if (GeneralUtils::endsWith(path, '/')) { @@ -393,7 +392,7 @@ void HttpServer::start(uint16_t portNumber, bool useSSL) { // Take the semaphore that says that we are now running. If we are already running, then end here as // there is nothing further to do. - if (m_semaphoreServerStarted.take(100, "start") == false) { + if (!m_semaphoreServerStarted.take(100, "start")) { ESP_LOGD(LOG_TAG, "<< start: Already running"); return; } @@ -428,7 +427,7 @@ void HttpServer::stop() { * @param [in] pathPattern The path pattern to be matched. * @param [in] webServerRequestHandler The request handler to be called. */ -PathHandler::PathHandler(std::string method, std::regex *pRegex, +PathHandler::PathHandler(std::string method, std::regex* pRegex, void (*pWebServerRequestHandler) ( HttpRequest* pHttpRequest, @@ -471,12 +470,9 @@ PathHandler::PathHandler(std::string method, std::string matchPath, * @return True if the path matches. */ bool PathHandler::match(std::string method, std::string path) { - if (method != m_method) { - return false; - } + if (method != m_method) return false; if (m_isRegex) { ESP_LOGD("PathHandler", "regex matching: %s with %s", m_textPattern.c_str(), path.c_str()); - return std::regex_search(path, *m_pRegex); } ESP_LOGD("PathHandler", "plain matching: %s with %s", m_textPattern.c_str(), path.c_str()); @@ -490,10 +486,6 @@ bool PathHandler::match(std::string method, std::string path) { * @param [in] response An object representing the response. * @return N/A. */ -void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse *response) { +void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse* response) { m_pRequestHandler(request, response); } // invokePathHandler - - - - diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index 95558c05..8baa7bbb 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -83,7 +83,7 @@ class HttpServer { void setDirectoryListing(bool use); // Should we list the content of directories? void setFileBufferSize(size_t fileBufferSize); // Set the size of the file buffer void setRootPath(std::string path); // Set the root of the file system path. - void start(uint16_t portNumber, bool useSSL=false); + void start(uint16_t portNumber, bool useSSL = false); void stop(); // Stop a previously started server. private: diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index dfcfeff0..5013956f 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -67,7 +67,7 @@ void I2C::endTransaction() { ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000/portTICK_PERIOD_MS); + errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000 / portTICK_PERIOD_MS); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_cmd_begin: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } @@ -81,8 +81,7 @@ void I2C::endTransaction() { * * @return The address of the %I2C slave. */ -uint8_t I2C::getAddress() const -{ +uint8_t I2C::getAddress() const { return m_address; } @@ -136,7 +135,7 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=%d, ack=%d)", length, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); if (errRc != ESP_OK) { @@ -157,11 +156,11 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { * @param [in] ack Whether or not we should send an ACK to the slave after reading a byte. * @return N/A. */ -void I2C::read(uint8_t *byte, bool ack) { +void I2C::read(uint8_t* byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=1, ack=%d)", ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); if (errRc != ESP_OK) { @@ -180,12 +179,11 @@ void I2C::read(uint8_t *byte, bool ack) { * @return N/A. */ void I2C::scan() { - uint8_t i; printf("Data Pin: %d, Clock Pin: %d\n", this->m_sdaPin, this->m_sclPin); printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); printf("00: "); - for (i=3; i<0x78; i++) { - if (i%16 == 0) { + for (uint8_t i = 3; i < 0x78; i++) { + if (i % 16 == 0) { printf("\n%.2x:", i); } if (slavePresent(i)) { @@ -203,8 +201,7 @@ void I2C::scan() { * * @param [in] address The address of the %I2C slave. */ -void I2C::setAddress(uint8_t address) -{ +void I2C::setAddress(uint8_t address) { this->m_address = address; } // setAddress @@ -230,7 +227,7 @@ bool I2C::slavePresent(uint8_t address) { ::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); ::i2c_master_stop(cmd); - esp_err_t espRc = ::i2c_master_cmd_begin(m_portNum, cmd, 100/portTICK_PERIOD_MS); + esp_err_t espRc = ::i2c_master_cmd_begin(m_portNum, cmd, 100 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); return espRc == 0; // Return true if the slave is present and false otherwise. } // slavePresent @@ -305,7 +302,7 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(length=%d, ack=%d)", length, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); if (errRc != ESP_OK) { @@ -317,5 +314,3 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { ESP_LOGE(LOG_TAG, "i2c_master_write: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } // write - - diff --git a/cpp_utils/I2C.h b/cpp_utils/I2C.h index 9886d63a..da065d91 100644 --- a/cpp_utils/I2C.h +++ b/cpp_utils/I2C.h @@ -17,14 +17,6 @@ * @brief Interface to %I2C functions. */ class I2C { -private: - uint8_t m_address; - i2c_cmd_handle_t m_cmd; - bool m_directionKnown; - gpio_num_t m_sdaPin; - gpio_num_t m_sclPin; - i2c_port_t m_portNum; - public: /** * @brief The default SDA pin. @@ -46,16 +38,25 @@ class I2C { void endTransaction(); uint8_t getAddress() const; void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0); - void read(uint8_t* bytes, size_t length, bool ack=true); - void read(uint8_t* byte, bool ack=true); + void read(uint8_t* bytes, size_t length, bool ack = true); + void read(uint8_t* byte, bool ack = true); void scan(); void setAddress(uint8_t address); void setDebug(bool enabled); bool slavePresent(uint8_t address); void start(); void stop(); - void write(uint8_t byte, bool ack=true); - void write(uint8_t* bytes, size_t length, bool ack=true); + void write(uint8_t byte, bool ack = true); + void write(uint8_t* bytes, size_t length, bool ack = true); + +private: + uint8_t m_address; + i2c_cmd_handle_t m_cmd; + bool m_directionKnown; + gpio_num_t m_sdaPin; + gpio_num_t m_sclPin; + i2c_port_t m_portNum; + }; #endif /* MAIN_I2C_H_ */ diff --git a/cpp_utils/I2S.cpp b/cpp_utils/I2S.cpp index c5bcb997..15ba2fa7 100644 --- a/cpp_utils/I2S.cpp +++ b/cpp_utils/I2S.cpp @@ -27,7 +27,6 @@ static const char* LOG_TAG = "I2S"; static intr_handle_t i2s_intr_handle; - /** * A representation of a DMA buffer. */ @@ -67,6 +66,7 @@ DMABuffer::~DMABuffer() { delete[] m_desc.buf; } // ~DMABuffer + /** * @brief Dump the state of the buffer. * @return N/A @@ -74,7 +74,7 @@ DMABuffer::~DMABuffer() { void DMABuffer::dump() { std::ostringstream ss; ss << "size: " << m_desc.size; - ss << ", buf: 0x" << std::hex << (uint32_t)m_desc.buf << std::dec; + ss << ", buf: 0x" << std::hex << (uint32_t) m_desc.buf << std::dec; ss << ", length: " << m_desc.length; ss << ", offset: " << m_desc.offset; ss << ", sosf: " << m_desc.sosf; @@ -82,10 +82,8 @@ void DMABuffer::dump() { ss << ", owner: " << m_desc.owner; ESP_LOGD(LOG_TAG, "Desc: %s", ss.str().c_str()); int length = 100; - if (length > m_desc.length) { - length = m_desc.length; - } - GeneralUtils::hexDump((uint8_t*)m_desc.buf, length); + if (length > m_desc.length) length = m_desc.length; + GeneralUtils::hexDump((uint8_t*) m_desc.buf, length); } /** @@ -95,23 +93,15 @@ void DMABuffer::dump() { * @return The number of bytes actually copied. */ uint32_t DMABuffer::getData(uint8_t* pData, uint32_t length) { - uint8_t* pBuf = (uint8_t*)m_desc.buf; - if (length > getLength()) { - length = getLength(); - } + uint8_t* pBuf = (uint8_t*) m_desc.buf; + if (length > getLength()) length = getLength(); uint32_t i; - // // The descriptor buffer is filled with data that contains: - // // b1 00 b0 00 b3 00 b2 00 b5 00 b4 00 b7 00 b6 00 - // // Our goal is to populate the passed in buffer with data of the form: - // // b0 b1 b2 b3 b4 b5 b6 b7 ... - // // The following alogrithm does that. - // - for (i=0; igetDesc()); - pCurrentDMABuffer = pCurrentDMABuffer->getNext(); - I2S0.int_clr.val = I2S0.int_raw.val; - pI2S->m_dmaSemaphore.giveFromISR(); +static void IRAM_ATTR i2s_isr(void* arg) { + I2S* pI2S = (I2S*) arg; + //ESP_EARLY_LOGV(LOG_TAG, "I2S isr"); + pLastDMABuffer = pCurrentDMABuffer; + //logI2SIntr(); + //logDesc(pCurrentDMABuffer->getDesc()); + pCurrentDMABuffer = pCurrentDMABuffer->getNext(); + I2S0.int_clr.val = I2S0.int_raw.val; + pI2S->m_dmaSemaphore.giveFromISR(); } /** @@ -261,20 +246,20 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { const uint32_t const_high = 0x38; - gpio_matrix_in(config.pin_d0, I2S0I_DATA_IN0_IDX, false); - gpio_matrix_in(config.pin_d1, I2S0I_DATA_IN1_IDX, false); - gpio_matrix_in(config.pin_d2, I2S0I_DATA_IN2_IDX, false); - gpio_matrix_in(config.pin_d3, I2S0I_DATA_IN3_IDX, false); - gpio_matrix_in(config.pin_d4, I2S0I_DATA_IN4_IDX, false); - gpio_matrix_in(config.pin_d5, I2S0I_DATA_IN5_IDX, false); - gpio_matrix_in(config.pin_d6, I2S0I_DATA_IN6_IDX, false); - gpio_matrix_in(config.pin_d7, I2S0I_DATA_IN7_IDX, false); - gpio_matrix_in(config.pin_vsync, I2S0I_V_SYNC_IDX, true); - gpio_matrix_in(config.pin_href, I2S0I_H_SYNC_IDX, false); - //gpio_matrix_in(const_high, I2S0I_V_SYNC_IDX, false); - //gpio_matrix_in(const_high, I2S0I_H_SYNC_IDX, false); - gpio_matrix_in(const_high, I2S0I_H_ENABLE_IDX, false); - gpio_matrix_in(config.pin_pclk, I2S0I_WS_IN_IDX, false); + gpio_matrix_in(config.pin_d0, I2S0I_DATA_IN0_IDX, false); + gpio_matrix_in(config.pin_d1, I2S0I_DATA_IN1_IDX, false); + gpio_matrix_in(config.pin_d2, I2S0I_DATA_IN2_IDX, false); + gpio_matrix_in(config.pin_d3, I2S0I_DATA_IN3_IDX, false); + gpio_matrix_in(config.pin_d4, I2S0I_DATA_IN4_IDX, false); + gpio_matrix_in(config.pin_d5, I2S0I_DATA_IN5_IDX, false); + gpio_matrix_in(config.pin_d6, I2S0I_DATA_IN6_IDX, false); + gpio_matrix_in(config.pin_d7, I2S0I_DATA_IN7_IDX, false); + gpio_matrix_in(config.pin_vsync, I2S0I_V_SYNC_IDX, true); + gpio_matrix_in(config.pin_href, I2S0I_H_SYNC_IDX, false); +// gpio_matrix_in(const_high, I2S0I_V_SYNC_IDX, false); +// gpio_matrix_in(const_high, I2S0I_H_SYNC_IDX, false); + gpio_matrix_in(const_high, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(config.pin_pclk, I2S0I_WS_IN_IDX, false); // Enable and configure I2S peripheral periph_module_enable(PERIPH_I2S0_MODULE); @@ -345,10 +330,10 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { ESP_LOGD(LOG_TAG, "Initializing %d descriptors", desc_count); - DMABuffer *pFirst = new DMABuffer(); - DMABuffer *pLast = pFirst; - for (int i=1; isetNext(pNewDMABuffer); pLast = pNewDMABuffer; } @@ -356,45 +341,45 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { pCurrentDMABuffer = pFirst; // I2S_RX_EOF_NUM_REG - I2S0.rx_eof_num = sample_count; - - // I2S_IN_LINK_REG -> I2S_INLINK_ADDR - I2S0.in_link.addr = (uint32_t) pFirst; - - // I2S_IN_LINK_REG -> I2S_INLINK_START - I2S0.in_link.start = 1; - - I2S0.int_clr.val = I2S0.int_raw.val; - I2S0.int_ena.val = 0; - I2S0.int_ena.in_done = 1; - - // Register the interrupt handler. - esp_intr_alloc( - ETS_I2S0_INTR_SOURCE, - ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, - &i2s_isr, - this, - &i2s_intr_handle); - - m_dmaSemaphore.take(); - // Start the interrupt handler - esp_intr_enable(i2s_intr_handle); - - I2S0.conf.rx_start = 1; - - /* - while(1) { - m_dmaSemaphore.wait(); - uint32_t dataLength = pLastDMABuffer->getLength(); - ESP_LOGD(LOG_TAG, "Got a DMA buffer; length=%d", dataLength); - //pLastDMABuffer->dump(); - uint8_t *pData = new uint8_t[dataLength]; - pLastDMABuffer->getData(pData, dataLength); + I2S0.rx_eof_num = sample_count; + + // I2S_IN_LINK_REG -> I2S_INLINK_ADDR + I2S0.in_link.addr = (uint32_t) pFirst; + + // I2S_IN_LINK_REG -> I2S_INLINK_START + I2S0.in_link.start = 1; + + I2S0.int_clr.val = I2S0.int_raw.val; + I2S0.int_ena.val = 0; + I2S0.int_ena.in_done = 1; + + // Register the interrupt handler. + esp_intr_alloc( + ETS_I2S0_INTR_SOURCE, + ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, + &i2s_isr, + this, + &i2s_intr_handle); + + m_dmaSemaphore.take(); + // Start the interrupt handler + esp_intr_enable(i2s_intr_handle); + + I2S0.conf.rx_start = 1; + + /* + while(1) { + m_dmaSemaphore.wait(); + uint32_t dataLength = pLastDMABuffer->getLength(); + ESP_LOGD(LOG_TAG, "Got a DMA buffer; length=%d", dataLength); + //pLastDMABuffer->dump(); + uint8_t *pData = new uint8_t[dataLength]; + pLastDMABuffer->getData(pData, dataLength); GeneralUtils::hexDump(pData, dataLength); delete[] pData; m_dmaSemaphore.take(); - } - */ + } + */ ESP_LOGD(LOG_TAG, "<< cameraMode"); } diff --git a/cpp_utils/IFTTT.cpp b/cpp_utils/IFTTT.cpp index 0cf7a6d9..6af2279d 100644 --- a/cpp_utils/IFTTT.cpp +++ b/cpp_utils/IFTTT.cpp @@ -37,7 +37,7 @@ void IFTTT::trigger( std::string value2, std::string value3) { m_restClient.setURL("https://maker.ifttt.com/trigger/" + event + "/with/key/" + m_key); - cJSON *root; + cJSON* root; root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "value1", value1.c_str()); diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index c55f3a78..31f8cdb1 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -125,11 +125,8 @@ void JsonArray::addString(std::string value) { * @return The boolean value at the given index. */ bool JsonArray::getBoolean(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); - if (node->valueint == 0) { - return false; - } - return true; + cJSON* node = cJSON_GetArrayItem(m_node, item); + return (node->valueint != 0); } // getBoolean @@ -139,7 +136,7 @@ bool JsonArray::getBoolean(int item) { * @return The double value at the given index. */ double JsonArray::getDouble(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return node->valuedouble; } // getDouble @@ -150,7 +147,7 @@ double JsonArray::getDouble(int item) { * @return The int value at the given index. */ int JsonArray::getInt(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return node->valueint; } // getInt @@ -161,7 +158,7 @@ int JsonArray::getInt(int item) { * @return The object value at the given index. */ JsonObject JsonArray::getObject(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return JsonObject(node); } // getObject @@ -172,7 +169,7 @@ JsonObject JsonArray::getObject(int item) { * @return The object value at the given index. */ std::string JsonArray::getString(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return std::string(node->valuestring); } // getString @@ -182,7 +179,7 @@ std::string JsonArray::getString(int item) { * @return A JSON string representation of the array. */ std::string JsonArray::toString() { - char *data = cJSON_Print(m_node); + char* data = cJSON_Print(m_node); std::string ret(data); free(data); return ret; @@ -194,7 +191,7 @@ std::string JsonArray::toString() { * @return A string representation. */ std::string JsonArray::toStringUnformatted() { - char *data = cJSON_PrintUnformatted(m_node); + char* data = cJSON_PrintUnformatted(m_node); std::string ret(data); free(data); return ret; @@ -217,7 +214,7 @@ JsonObject::JsonObject(cJSON* node) { } // JsonObject JsonArray JsonObject::getArray(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); return JsonArray(node); } @@ -228,10 +225,8 @@ JsonArray JsonObject::getArray(std::string name) { * @return The boolean value from the object. */ bool JsonObject::getBoolean(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node == nullptr) { - return false; - } + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return false; return cJSON_IsTrue(node); } // getBoolean @@ -242,10 +237,8 @@ bool JsonObject::getBoolean(std::string name) { * @return The double value from the object. */ double JsonObject::getDouble(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node == nullptr) { - return 0.0; - } + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return 0.0; return node->valuedouble; } // getDouble @@ -256,10 +249,8 @@ double JsonObject::getDouble(std::string name) { * @return The int value from the object. */ int JsonObject::getInt(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node == nullptr) { - return 0; - } + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return 0; return node->valueint; } // getInt @@ -270,7 +261,7 @@ int JsonObject::getInt(std::string name) { * @return The object value from the object. */ JsonObject JsonObject::getObject(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); return JsonObject(node); } // getObject @@ -281,10 +272,8 @@ JsonObject JsonObject::getObject(std::string name) { * @return The string value from the object. A zero length string is returned when the object is not present. */ std::string JsonObject::getString(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node == nullptr) { - return ""; - } + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return ""; return std::string(node->valuestring); } // getString @@ -325,7 +314,7 @@ void JsonObject::setArray(std::string name, JsonArray array) { * @return N/A. */ void JsonObject::setBoolean(std::string name, bool value) { - cJSON_AddItemToObject(m_node, name.c_str(), value?cJSON_CreateTrue():cJSON_CreateFalse()); + cJSON_AddItemToObject(m_node, name.c_str(), value ? cJSON_CreateTrue() : cJSON_CreateFalse()); } // setBoolean @@ -347,7 +336,7 @@ void JsonObject::setDouble(std::string name, double value) { * @return N/A. */ void JsonObject::setInt(std::string name, int value) { - cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateNumber((double)value)); + cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateNumber((double) value)); } // setInt @@ -378,7 +367,7 @@ void JsonObject::setString(std::string name, std::string value) { * @return A JSON string representation of the object. */ std::string JsonObject::toString() { - char *data = cJSON_Print(m_node); + char* data = cJSON_Print(m_node); std::string ret(data); free(data); return ret; @@ -390,7 +379,7 @@ std::string JsonObject::toString() { * @return A string representation. */ std::string JsonObject::toStringUnformatted() { - char *data = cJSON_PrintUnformatted(m_node); + char* data = cJSON_PrintUnformatted(m_node); std::string ret(data); free(data); return ret; diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index 13f8f0a2..f132a0a7 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -25,6 +25,7 @@ class JSON { static void deleteArray(JsonArray jsonArray); static JsonObject parseObject(std::string text); static JsonArray parseArray(std::string text); + }; // JSON @@ -33,7 +34,6 @@ class JSON { */ class JsonArray { public: - int getInt(int item); JsonObject getObject(int item); std::string getString(int item); @@ -47,6 +47,7 @@ class JsonArray { std::string toString(); std::string toStringUnformatted(); std::size_t size(); + private: JsonArray(cJSON* node); friend class JSON; @@ -54,7 +55,8 @@ class JsonArray { /** * @brief The underlying cJSON node. */ - cJSON *m_node; + cJSON* m_node; + }; // JsonArray @@ -88,6 +90,7 @@ class JsonObject { * @brief The underlying cJSON node. */ cJSON* m_node; + }; // JsonObject diff --git a/cpp_utils/MAX7219.cpp b/cpp_utils/MAX7219.cpp index d83615d9..4cac347d 100644 --- a/cpp_utils/MAX7219.cpp +++ b/cpp_utils/MAX7219.cpp @@ -76,7 +76,7 @@ const static uint8_t charTable[] = { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }; -MAX7219::MAX7219(SPI *spi, int numDevices) { +MAX7219::MAX7219(SPI* spi, int numDevices) { assert(spi != nullptr); this->spi = spi; if (numDevices <= 0 || numDevices > 8) { @@ -84,7 +84,7 @@ MAX7219::MAX7219(SPI *spi, int numDevices) { } maxDevices = numDevices; - for (int i = 0; i < 64; i++) { + for (uint8_t i = 0; i < 64; i++) { status[i] = 0x00; } for (int i = 0; i < maxDevices; i++) { @@ -106,21 +106,18 @@ int MAX7219::getDeviceCount() { void MAX7219::shutdown(bool b, int addr) { - if (addr < 0 || addr >= maxDevices) - return; + if (addr < 0 || addr >= maxDevices) return; + if (b) { spiTransfer(addr, OP_SHUTDOWN, 0); - } - else { + } else { spiTransfer(addr, OP_SHUTDOWN, 1); } } void MAX7219::setScanLimit(int limit, int addr) { - if (addr < 0 || addr >= maxDevices) { - return; - } + if (addr < 0 || addr >= maxDevices) return; if (limit >= 0 && limit < 8) { spiTransfer(addr, OP_SCANLIMIT, limit); } @@ -128,9 +125,7 @@ void MAX7219::setScanLimit(int limit, int addr) { void MAX7219::setIntensity(int intensity, int addr) { - if (addr < 0 || addr >= maxDevices) { - return; - } + if (addr < 0 || addr >= maxDevices) return; if (intensity >= 0 && intensity < 16) { spiTransfer(addr, OP_INTENSITY, intensity); } @@ -138,13 +133,11 @@ void MAX7219::setIntensity(int intensity, int addr) { void MAX7219::clearDisplay(int addr) { - int offset; + if (addr < 0 || addr >= maxDevices) return; - if (addr < 0 || addr >= maxDevices) { - return; - } + int offset; offset = addr * 8; - for (int i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { status[offset + i] = 0; spiTransfer(addr, i + 1, status[offset + i]); } @@ -152,12 +145,11 @@ void MAX7219::clearDisplay(int addr) { void MAX7219::setLed(int row, int column, bool state, int addr) { + if (addr < 0 || addr >= maxDevices) return; + int offset; - uint8_t val = 0x00; + uint8_t val = 0; - if (addr < 0 || addr >= maxDevices) { - return; - } if (row < 0 || row > 7 || column < 0 || column > 7) { return; } @@ -165,8 +157,7 @@ void MAX7219::setLed(int row, int column, bool state, int addr) { val = 0b10000000 >> column; if (state) { status[offset + row] = status[offset + row] | val; - } - else { + } else { val = ~val; status[offset + row] = status[offset + row] & val; } @@ -175,28 +166,20 @@ void MAX7219::setLed(int row, int column, bool state, int addr) { void MAX7219::setRow(int row, uint8_t value, int addr) { - int offset; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (row < 0 || row > 7) { - return; - } - offset = addr * 8; + if (addr < 0 || addr >= maxDevices) return; + if (row < 0 || row > 7) return; + + int offset = addr * 8; status[offset + row] = value; spiTransfer(addr, row + 1, status[offset + row]); } void MAX7219::setColumn(int col, uint8_t value, int addr) { - uint8_t val; + if (addr < 0 || addr >= maxDevices) return; + if (col < 0 || col > 7) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (col < 0 || col > 7) { - return; - } + uint8_t val; for (int row = 0; row < 8; row++) { val = value >> (7 - row); val = val & 0x01; @@ -206,17 +189,11 @@ void MAX7219::setColumn(int col, uint8_t value, int addr) { void MAX7219::setDigit(int digit, uint8_t value, bool dp, int addr) { - int offset; - uint8_t v; + if (addr < 0 || addr >= maxDevices) return; + if (digit < 0 || digit > 7 || value > 15) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (digit < 0 || digit > 7 || value > 15) { - return; - } - offset = addr * 8; - v = charTable[value]; + int offset = addr * 8; + uint8_t v = charTable[value]; if (dp) { v |= 0b10000000; } @@ -226,22 +203,13 @@ void MAX7219::setDigit(int digit, uint8_t value, bool dp, int addr) { void MAX7219::setChar(int digit, char value, bool dp, int addr) { - int offset; - uint8_t index, v; + if (addr < 0 || addr >= maxDevices) return; + if (digit < 0 || digit > 7) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (digit < 0 || digit > 7) { - return; - } - offset = addr * 8; - index = (uint8_t) value; - if (index > 127) { - //no defined beyond index 127, so we use the space char - index = 32; - } - v = charTable[index]; + int offset = addr * 8; + uint8_t index = (uint8_t) value; + if (index > 127) index = 32; // not defined beyond index 127, so we use the space char + uint8_t v = charTable[index]; if (dp) { v |= 0b10000000; } @@ -250,7 +218,7 @@ void MAX7219::setChar(int digit, char value, bool dp, int addr) { } -void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t data) { +void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t data) { //Create an array with the data to shift out int offset = addr * 2; int maxbytes = maxDevices * 2; @@ -268,13 +236,13 @@ void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t da } void MAX7219::setNumber(uint32_t number, int addr) { - //number = number % (uint32_t)pow(10, maxDevices); - for (auto i=0; i<8; i++) { - if (number == 0 && i > 0){ + // number = number % (uint32_t) pow(10, maxDevices); + for (uint8_t i = 0; i < 8; i++) { + if (number == 0 && i > 0) { setChar(i, ' ', addr); } else { - setDigit(i, number%10, addr); - number = number/10; + setDigit(i, number % 10, addr); + number = number / 10; } } } diff --git a/cpp_utils/MAX7219.h b/cpp_utils/MAX7219.h index b5ae41f4..d3faf353 100644 --- a/cpp_utils/MAX7219.h +++ b/cpp_utils/MAX7219.h @@ -50,18 +50,6 @@ * and setColumn(). */ class MAX7219 { -private: - - /* Send out a single command to the device */ - void spiTransfer(int addr, uint8_t opcode, uint8_t data); - - /* We keep track of the led-status for all 8 devices in this array */ - uint8_t status[64]; - - /* The maximum number of devices we use */ - int maxDevices; - SPI *spi; - public: /** * @brief Create a new %MAX7219 controller @@ -70,7 +58,7 @@ class MAX7219 { * @param [in] numDevices maximum number of devices that can be controlled that are * daisy chained together. */ - MAX7219(SPI *spi, int numDevices = 1); + MAX7219(SPI* spi, int numDevices = 1); /** @@ -101,7 +89,7 @@ class MAX7219 { * @param [in] dp Sets the decimal point. * @param [in] addr Address of the display. */ - void setChar(int digit, char value, bool dp=false, int addr=0); + void setChar(int digit, char value, bool dp=false, int addr = 0); /** @@ -111,7 +99,7 @@ class MAX7219 { * @param [in] value each bit set to 1 will light up the corresponding Led. * @param [in] addr address of the display. */ - void setColumn(int col, uint8_t value, int addr=0); + void setColumn(int col, uint8_t value, int addr = 0); /** @@ -122,7 +110,7 @@ class MAX7219 { * @param [in] dp Sets the decimal point. * @param [in] addr Address of the display. */ - void setDigit(int digit, uint8_t value, bool dp=false, int addr=0); + void setDigit(int digit, uint8_t value, bool dp=false, int addr = 0); /** @@ -131,7 +119,7 @@ class MAX7219 { * @param [in] intensity the brightness of the display. (0..15). * @param [in] addr The address of the display to control. */ - void setIntensity(int intensity, int addr=0); + void setIntensity(int intensity, int addr = 0); /** @@ -142,7 +130,7 @@ class MAX7219 { * @param [in] state If true the led is switched on, if false it is switched off. * @param [in] addr Address of the display. */ - void setLed(int row, int col, bool state, int addr=0); + void setLed(int row, int col, bool state, int addr = 0); /** @@ -153,7 +141,7 @@ class MAX7219 { * @param [in] number The number to display. * @param [in] addr Address of the display. */ - void setNumber(uint32_t number, int addr=0); + void setNumber(uint32_t number, int addr = 0); /** @@ -163,7 +151,7 @@ class MAX7219 { * @param [in] value Each bit set to 1 will light up the corresponding Led. * @param [in] addr Address of the display. */ - void setRow(int row, uint8_t value, int addr=0); + void setRow(int row, uint8_t value, int addr = 0); /** @@ -175,7 +163,7 @@ class MAX7219 { * @param [in] limit Number of digits to be displayed (1..8). * @param [in] addr Address of the display to control. */ - void setScanLimit(int limit, int addr=0); + void setScanLimit(int limit, int addr = 0); /** @@ -185,7 +173,18 @@ class MAX7219 { * for normal operation. * @param [in] addr The address of the display to control. */ - void shutdown(bool status, int addr=0); + void shutdown(bool status, int addr = 0); + +private: + /* Send out a single command to the device */ + void spiTransfer(int addr, uint8_t opcode, uint8_t data); + + /* We keep track of the led-status for all 8 devices in this array */ + uint8_t status[64]; + + /* The maximum number of devices we use */ + int maxDevices; + SPI* spi; }; diff --git a/cpp_utils/MFRC522.cpp b/cpp_utils/MFRC522.cpp index a860e9ba..6feb57c1 100644 --- a/cpp_utils/MFRC522.cpp +++ b/cpp_utils/MFRC522.cpp @@ -41,29 +41,29 @@ static const char LOG_TAG[] = "MFRC522"; /** * Writes a byte to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to write to. One of the PCD_Register enums. + * @param value The value to write. */ -void MFRC522::PCD_WriteRegister( - PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. - byte value ///< The value to write. - ) { +void MFRC522::PCD_WriteRegister(PCD_Register reg, byte value) { uint8_t data[2]; data[0] = reg; data[1] = value; m_spi.transfer(data, 2); } // End PCD_WriteRegister() + /** * Writes a number of bytes to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to write to. One of the PCD_Register enums. + * @param count The number of bytes to write to the register + * @param values The values to write. Byte array. */ -void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. - byte count, ///< The number of bytes to write to the register - byte *values ///< The values to write. Byte array. - ) { - uint8_t* pData = new uint8_t[count+1]; +void MFRC522::PCD_WriteRegister(PCD_Register reg, byte count, byte* values) { + uint8_t* pData = new uint8_t[count + 1]; pData[0] = reg; - memcpy(pData+1, values, count); - m_spi.transfer(pData, count+1); + memcpy(pData + 1, values, count); + m_spi.transfer(pData, count + 1); delete[] pData; } // End PCD_WriteRegister() @@ -71,9 +71,9 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to /** * Reads a byte from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to read from. One of the PCD_Register enums. */ -byte MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. - ) { +byte MFRC522::PCD_ReadRegister(PCD_Register reg) { uint8_t data[2]; data[0] = reg | 0x80; data[1] = 0; @@ -81,18 +81,18 @@ byte MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. return data[1]; } // End PCD_ReadRegister() + /** * Reads a number of bytes from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to read from. One of the PCD_Register enums. + * @param count The number of bytes to read + * @param values Byte array to store the values in. + * @param rxAlign Only bit positions rxAlign..7 in values[0] are updated. */ -void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from. One of the PCD_Register enums. - byte count, ///< The number of bytes to read - byte *values, ///< Byte array to store the values in. - byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. - ) { - if (count == 0) { - return; - } +void MFRC522::PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte rxAlign) { + if (count == 0) return; + //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); byte address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. byte index = 0; // Index in values array. @@ -114,23 +114,25 @@ void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from values[index] = m_spi.transferByte(0); // Read the final byte. Send 0 to stop reading. } // End PCD_ReadRegister() + /** * Sets the bits given in mask in register reg. + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to set. */ -void MFRC522::PCD_SetRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to set. - ) { +void MFRC522::PCD_SetRegisterBitMask(PCD_Register reg, byte mask) { byte tmp; tmp = PCD_ReadRegister(reg); - PCD_WriteRegister(reg, tmp | mask); // set bit mask + PCD_WriteRegister(reg, tmp | mask); // set bit mask } // End PCD_SetRegisterBitMask() + /** * Clears the bits given in mask from register reg. + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to clear. */ -void MFRC522::PCD_ClearRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to clear. - ) { +void MFRC522::PCD_ClearRegisterBitMask(PCD_Register reg, byte mask) { byte tmp; tmp = PCD_ReadRegister(reg); PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask @@ -139,13 +141,12 @@ void MFRC522::PCD_ClearRegisterBitMask( PCD_Register reg, ///< The register to u /** * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. - * + * @param data In: Pointer to the data to transfer to the FIFO for CRC calculation. + * @param length In: The number of bytes to transfer. + * @param result Out: Pointer to result buffer. Result is written to result[0..1], low byte first. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. - byte length, ///< In: The number of bytes to transfer. - byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. - ) { +MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte *data, byte length, byte *result) { PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit PCD_WriteRegister(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization @@ -160,6 +161,7 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved byte n = PCD_ReadRegister(DivIrqReg); if (n & 0x04) { // CRCIRq bit set - calculation done + if (n & 0x04) { // CRCIRq bit set - calculation done PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. // Transfer the result from the registers to the result buffer result[0] = PCD_ReadRegister(CRCResultRegL); @@ -172,9 +174,8 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to } // End PCD_CalculateCRC() -///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 -///////////////////////////////////////////////////////////////////////////////////// + /** * Initializes the MFRC522 chip. @@ -185,19 +186,17 @@ void MFRC522::PCD_Init() { bool hardReset = false; - // Set the chipSelectPin as digital output, do not select the slave yet -/* - pinMode(_chipSelectPin, OUTPUT); - digitalWrite(_chipSelectPin, HIGH); -*/ + // pinMode(_chipSelectPin, OUTPUT); + // digitalWrite(_chipSelectPin, HIGH); + // If a valid pin number has been set, pull device out of power down / reset state. if (_resetPowerDownPin != UNUSED_PIN) { // Set the resetPowerDownPin as digital output, do not reset or power down. //pinMode(_resetPowerDownPin, OUTPUT); ESP32CPP::GPIO::setInput((gpio_num_t)_resetPowerDownPin); - if (ESP32CPP::GPIO::read((gpio_num_t)_resetPowerDownPin) == false) { // The MFRC522 chip is in power down mode. + if (!ESP32CPP::GPIO::read((gpio_num_t)_resetPowerDownPin)) { // The MFRC522 chip is in power down mode. ESP32CPP::GPIO::setOutput((gpio_num_t)_resetPowerDownPin); ESP32CPP::GPIO::high((gpio_num_t)_resetPowerDownPin); // Exit power down mode. This triggers a hard reset. // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let us be generous: 50ms. @@ -221,27 +220,30 @@ void MFRC522::PCD_Init() { // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds - PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. PCD_WriteRegister(TReloadRegL, 0xE8); - PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) - PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) } // End PCD_Init() + /** * Initializes the MFRC522 chip. + * @param chipSelectPin Pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + * @param resetPowerDownPin Pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) */ -void MFRC522::PCD_Init( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) - byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) - ) { +void MFRC522::PCD_Init(byte chipSelectPin, byte resetPowerDownPin) { _chipSelectPin = chipSelectPin; _resetPowerDownPin = resetPowerDownPin; // Set the chipSelectPin as digital output, do not select the slave yet PCD_Init(); } // End PCD_Init() + /** * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. */ @@ -252,11 +254,12 @@ void MFRC522::PCD_Reset() { // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let us be generous: 50ms. FreeRTOS::sleep(50); // Wait for the PowerDown bit in CommandReg to be cleared - while (PCD_ReadRegister(CommandReg) & (1<<4)) { + while (PCD_ReadRegister(CommandReg) & (1 << 4)) { // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. } } // End PCD_Reset() + /** * Turns the antenna on by enabling pins TX1 and TX2. * After a reset these pins are disabled. @@ -268,6 +271,7 @@ void MFRC522::PCD_AntennaOn() { } } // End PCD_AntennaOn() + /** * Turns the antenna off by disabling pins TX1 and TX2. */ @@ -275,6 +279,7 @@ void MFRC522::PCD_AntennaOff() { PCD_ClearRegisterBitMask(TxControlReg, 0x03); } // End PCD_AntennaOff() + /** * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf @@ -283,21 +288,23 @@ void MFRC522::PCD_AntennaOff() { * @return Value of the RxGain, scrubbed to the 3 bits used. */ byte MFRC522::PCD_GetAntennaGain() { - return PCD_ReadRegister(RFCfgReg) & (0x07<<4); + return PCD_ReadRegister(RFCfgReg) & (0x07 << 4); } // End PCD_GetAntennaGain() + /** * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. */ void MFRC522::PCD_SetAntennaGain(byte mask) { - if (PCD_GetAntennaGain() != mask) { // only bother if there is a change - PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern + if (PCD_GetAntennaGain() != mask) { // only bother if there is a change + PCD_ClearRegisterBitMask(RFCfgReg, (0x07 << 4)); // clear needed to allow 000 pattern PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits } } // End PCD_SetAntennaGain() + /** * Performs a self-test of the MFRC522 * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf @@ -335,9 +342,7 @@ bool MFRC522::PCD_PerformSelfTest() { // It is reported that some devices does not trigger CRCIRq flag // during selftest. n = PCD_ReadRegister(FIFOLevelReg); - if (n >= 64) { - break; - } + if (n >= 64) break; } PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. @@ -373,9 +378,7 @@ bool MFRC522::PCD_PerformSelfTest() { // Verify that the results match up to our expectations for (uint8_t i = 0; i < 64; i++) { - if (result[i] != (reference[i])) { - return false; - } + if (result[i] != (reference[i])) return false; } // Test passed; all is good. @@ -383,46 +386,48 @@ bool MFRC522::PCD_PerformSelfTest() { } // End PCD_PerformSelfTest() ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs ///////////////////////////////////////////////////////////////////////////////////// + /** * Executes the Transceive command. * CRC validation can only be done if backData and backLen are specified. - * + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData nullptr or pointer to buffer if data should be read back after executing the command. + * @param backLen In: Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default nullptr. + * @param rxAlign In: Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default nullptr. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { byte waitIRq = 0x30; // RxIRq and IdleIRq +MFRC522::StatusCode MFRC522::PCD_TransceiveData(byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); } // End PCD_TransceiveData() + /** * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. * CRC validation can only be done if backData and backLen are specified. * + * @param command The command to execute. One of the PCD_Command enums. + * @param waitIRq The bits in the ComIrqReg register that signals successful completion of the command. + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData nullptr or pointer to buffer if data should be read back after executing the command. + * @param backLen In: Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + * @param rxAlign In: Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. - byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. - byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { +MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC(byte command, byte waitIRq, byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { // Prepare values for BitFramingReg - byte txLastBits = validBits ? *validBits : 0; byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + byte txLastBits = validBits ? *validBits : (byte) 0; PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits @@ -449,9 +454,7 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co } } // 35.7ms and nothing happend. Communication with the MFRC522 might be down. - if (i == 0) { - return STATUS_TIMEOUT; - } + if (i == 0) return STATUS_TIMEOUT; // Stop now if any errors except collisions were detected. byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr @@ -464,10 +467,8 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co // If the caller wants data back, get it from the MFRC522. if (backData && backLen) { byte n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO - if (n > *backLen) { - return STATUS_NO_ROOM; - } - *backLen = n; // Number of bytes returned + if (n > *backLen) return STATUS_NO_ROOM; + *backLen = n; // Number of bytes returned PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. if (validBits) { @@ -483,19 +484,13 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co // Perform CRC_A validation if requested. if (backData && backLen && checkCRC) { // In this case a MIFARE Classic NAK is not OK. - if (*backLen == 1 && _validBits == 4) { - return STATUS_MIFARE_NACK; - } + if (*backLen == 1 && _validBits == 4) return STATUS_MIFARE_NACK; // We need at least the CRC_A value and all 8 bits of the last byte must be received. - if (*backLen < 2 || _validBits != 0) { - return STATUS_CRC_WRONG; - } + if (*backLen < 2 || _validBits != 0) return STATUS_CRC_WRONG; // Verify CRC_A - do our own calculation and store the control in controlBuffer. byte controlBuffer[2]; MFRC522::StatusCode status = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); - if (status != STATUS_OK) { - return status; - } + if (status != STATUS_OK) return status; if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { return STATUS_CRC_WRONG; } @@ -504,58 +499,57 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co return STATUS_OK; } // End PCD_CommunicateWithPICC() + /** * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_RequestA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_RequestA(byte* bufferATQA, byte* bufferSize) { return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); } // End PICC_RequestA() + /** * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_WakeupA(byte* bufferATQA, byte* bufferSize) { return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); } // End PICC_WakeupA() + /** * Transmits REQA or WUPA commands. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param command The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA - byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA(byte command, byte* bufferATQA, byte* bufferSize) { byte validBits; MFRC522::StatusCode status; - if (bufferATQA == nullptr || *bufferSize < 2) { // The ATQA response is 2 bytes long. - return STATUS_NO_ROOM; - } PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + if (bufferATQA == nullptr || *bufferSize < 2) return STATUS_NO_ROOM; // The ATQA response is 2 bytes long. status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); - if (status != STATUS_OK) { - return status; - } - if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. - return STATUS_ERROR; - } + if (status != STATUS_OK) return status; + + if (*bufferSize != 2 || validBits != 0) return STATUS_ERROR; // ATQA must be exactly 16 bits. return STATUS_OK; } // End PICC_REQA_or_WUPA() + /** * Transmits SELECT/ANTICOLLISION commands to select a single PICC. * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). @@ -573,9 +567,7 @@ MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command * * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. - byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. - ) { +MFRC522::StatusCode MFRC522::PICC_Select(Uid* uid, byte validBits) { bool uidComplete; bool selectDone; bool useCascadeTag; @@ -589,7 +581,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. - byte *responseBuffer; + byte* responseBuffer; byte responseLength; // Description of buffer structure: @@ -615,9 +607,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct // 3 uid6 uid7 uid8 uid9 // Sanity checks - if (validBits > 80) { - return STATUS_INVALID; - } + if (validBits > 80) return STATUS_INVALID; // Prepare MFRC522 PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. @@ -632,22 +622,18 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct uidIndex = 0; useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes break; - case 2: buffer[0] = PICC_CMD_SEL_CL2; uidIndex = 3; useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes break; - case 3: buffer[0] = PICC_CMD_SEL_CL3; uidIndex = 6; useCascadeTag = false; // Never used in CL3. break; - default: return STATUS_INTERNAL_ERROR; - break; } // How many UID bits are known in this Cascade Level? @@ -686,16 +672,13 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 7, &buffer[7]); - if (result != STATUS_OK) { - return result; - } txLastBits = 0; // 0 => All 8 bits are valid. bufferUsed = 9; + if (result != STATUS_OK) return result; // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) responseBuffer = &buffer[6]; responseLength = 3; - } - else { // This is an ANTICOLLISION. + } else { // This is an ANTICOLLISION. //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); txLastBits = currentLevelKnownBits % 8; count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. @@ -715,31 +698,25 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] - if (valueOfCollReg & 0x20) { // CollPosNotValid - return STATUS_COLLISION; // Without a valid collision position we cannot continue - } + if (valueOfCollReg & 0x20) return STATUS_COLLISION; // CollPosNotValid, Without a valid collision position we cannot continue byte collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32. if (collisionPos == 0) { collisionPos = 32; } - if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen - return STATUS_INTERNAL_ERROR; - } + if (collisionPos <= currentLevelKnownBits) return STATUS_INTERNAL_ERROR; // No progress - should not happen + // Choose the PICC with the bit set. currentLevelKnownBits = collisionPos; count = (currentLevelKnownBits - 1) % 8; // The bit to modify index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. buffer[index] |= (1 << count); } - else if (result != STATUS_OK) { - return result; - } + else if (result != STATUS_OK) return result; else { // STATUS_OK if (currentLevelKnownBits >= 32) { // This was a SELECT. selectDone = true; // No more anticollision // We continue below outside the while. - } - else { // This was an ANTICOLLISION. + } else { // This was an ANTICOLLISION. // We now have all 32 bits of the UID in this Cascade Level currentLevelKnownBits = 32; // Run loop again to do the SELECT. @@ -757,21 +734,18 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct } // Check response SAK (Select Acknowledge) - if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). - return STATUS_ERROR; - } + if (responseLength != 3 || txLastBits != 0) return STATUS_ERROR; // SAK must be exactly 24 bits (1 byte + CRC_A). + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { return STATUS_CRC_WRONG; } if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes cascadeLevel++; - } - else { + } else { uidComplete = true; uid->sak = responseBuffer[0]; } @@ -783,6 +757,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct return STATUS_OK; } // End PICC_Select() + /** * Instructs a PICC in state ACTIVE(*) to go to state HALT. * @@ -797,9 +772,7 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { buffer[1] = 0; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Send the command. // The standard says: @@ -807,12 +780,10 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { // HLTA command, this response shall be interpreted as 'not acknowledge'. // We interpret that this way: Only STATUS_TIMEOUT is a success. result = PCD_TransceiveData(buffer, sizeof(buffer), nullptr, 0); - if (result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) - return STATUS_ERROR; - } + + if (result == STATUS_TIMEOUT) return STATUS_OK; + if (result == STATUS_OK) return STATUS_ERROR; // That is ironically NOT ok in this case ;-) + return result; } // End PICC_HaltA() @@ -820,6 +791,7 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// + /** * Executes the MFRC522 MFAuthent command. * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. @@ -830,34 +802,35 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { * * All keys are set to FFFFFFFFFFFFh at chip delivery. * + * @param command PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + * @param blockAddr The block number. See numbering in the comments in the .h file. + * @param key Pointer to the Crypteo1 key to use (6 bytes) + * @param uid Pointer to Uid struct. The first 4 bytes of the UID is used. * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. */ -MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B - byte blockAddr, ///< The block number. See numbering in the comments in the .h file. - MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) - Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. - ) { byte waitIRq = 0x10; // IdleIRq +MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key* key, Uid* uid) { // Build command buffer byte sendData[12]; sendData[0] = command; sendData[1] = blockAddr; for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes - sendData[2+i] = key->keyByte[i]; + sendData[2 + i] = key->keyByte[i]; } // Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf // section 3.2.5 "MIFARE Classic Authentication". // The only missed case is the MF1Sxxxx shortcut activation, // but it requires cascade tag (CT) byte, that is not part of uid. - for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID - sendData[8+i] = uid->uidByte[i+uid->size-4]; + for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID + sendData[8 + i] = uid->uidByte[i + uid->size - 4]; } // Start the authentication. return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); } // End PCD_Authenticate() + /** * Used to exit the PCD from its authenticated state. * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. @@ -867,6 +840,7 @@ void MFRC522::PCD_StopCrypto1() { PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] } // End PCD_StopCrypto1() + /** * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. * @@ -881,32 +855,29 @@ void MFRC522::PCD_StopCrypto1() { * The buffer must be at least 18 bytes because a CRC_A is also returned. * Checks the CRC_A before returning STATUS_OK. * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + * @param buffer The buffer to store the data in + * @param bufferSize Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. - byte *buffer, ///< The buffer to store the data in - byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Read(byte blockAddr, byte* buffer, byte* bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || *bufferSize < 18) { - return STATUS_NO_ROOM; - } + if (buffer == nullptr || *bufferSize < 18) return STATUS_NO_ROOM; // Build command buffer buffer[0] = PICC_CMD_MF_READ; buffer[1] = blockAddr; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Transmit the buffer and receive the response, validate CRC_A. return PCD_TransceiveData(buffer, 4, buffer, bufferSize, nullptr, 0, true); } // End MIFARE_Read() + /** * Writes 16 bytes to the active PICC. * @@ -915,19 +886,16 @@ MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. - * * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + * @param buffer The 16 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. - byte *buffer, ///< The 16 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Write(byte blockAddr, byte* buffer, byte bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || bufferSize < 16) { - return STATUS_INVALID; - } + if (buffer == nullptr || bufferSize < 16) return STATUS_INVALID; // Mifare Classic protocol requires two communications to perform a write. // Step 1: Tell the PICC we want to write to block blockAddr. @@ -935,34 +903,28 @@ MFRC522::StatusCode MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: cmdBuffer[0] = PICC_CMD_MF_WRITE; cmdBuffer[1] = blockAddr; result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Step 2: Transfer the data result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_Write() + /** * Writes a 4 byte page to the active MIFARE Ultralight PICC. - * + * @param page The page (2-15) to write to. + * @param buffer The 4 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. - byte *buffer, ///< The 4 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write(byte page, byte* buffer, byte bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || bufferSize < 4) { - return STATUS_INVALID; - } + if (buffer == nullptr || bufferSize < 4) return STATUS_INVALID; // Build commmand buffer byte cmdBuffer[6]; @@ -972,106 +934,105 @@ MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page // Perform the write result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; + return STATUS_OK; } // End MIFARE_Ultralight_Write() + /** * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. + * @param blockAddr The block (0-0xff) number. + * @param delta This number is subtracted from the value of block blockAddr. * * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. - int32_t delta ///< This number is subtracted from the value of block blockAddr. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Decrement(byte blockAddr, int32_t delta) { return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); } // End MIFARE_Decrement() + /** * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. * + * @param blockAddr The block (0-0xff) number. + * @param delta This number is added to the value of block blockAddr. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. - int32_t delta ///< This number is added to the value of block blockAddr. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Increment(byte blockAddr, int32_t delta) { return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); } // End MIFARE_Increment() + /** * MIFARE Restore copies the value of the addressed block into a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. * + * @param blockAddr The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Restore(byte blockAddr) { // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. // Doing only a single step does not work, so I chose to transfer 0L in step two. return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); } // End MIFARE_Restore() + /** * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. * + * @param command The command to use + * @param blockAddr The block (0-0xff) number. + * @param data The data to transfer in step 2 * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use - byte blockAddr, ///< The block (0-0xff) number. - int32_t data ///< The data to transfer in step 2 - ) { +MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper(byte command, byte blockAddr, int32_t data) { MFRC522::StatusCode result; byte cmdBuffer[2]; // We only need room for 2 bytes. // Step 1: Tell the PICC the command and block address cmdBuffer[0] = command; cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) return result; // Step 2: Transfer the data - result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive((byte*) &data, 4, true); // Adds CRC_A and accept timeout as success. + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_TwoStepHelper() + /** * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * + * @param blockAddr The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Transfer(byte blockAddr) { MFRC522::StatusCode result; byte cmdBuffer[2]; // We only need room for 2 bytes. // Tell the PICC we want to transfer the result into block blockAddr. cmdBuffer[0] = PICC_CMD_MF_TRANSFER; cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_Transfer() + /** * Helper routine to read the current value from a Value Block. * @@ -1083,7 +1044,7 @@ MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0 * @param[out] value Current value of the Value Block. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { +MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t* value) { MFRC522::StatusCode status; byte buffer[18]; byte size = sizeof(buffer); @@ -1092,11 +1053,12 @@ MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { status = MIFARE_Read(blockAddr, buffer, &size); if (status == STATUS_OK) { // Extract the value - *value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); + *value = (int32_t(buffer[3]) << 24) | (int32_t(buffer[2]) << 16) | (int32_t(buffer[1]) << 8) | int32_t(buffer[0]); } return status; } // End MIFARE_GetValue() + /** * Helper routine to write a specific value into a Value Block. * @@ -1129,6 +1091,7 @@ MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { return MIFARE_Write(blockAddr, buffer, 16); } // End MIFARE_SetValue() + /** * Authenticate with a NTAG216. * @@ -1138,8 +1101,7 @@ MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { * @param[in] pACK result success???. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Authenticate with 32bit password -{ +MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) { //Authenticate with 32bit password // TODO: Fix cmdBuffer length and rxlength. They really should match. // (Better still, rxlength should not even be necessary.) @@ -1148,14 +1110,13 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut cmdBuffer[0] = 0x1B; //Comando de autentificacion - for (byte i = 0; i<4; i++) - cmdBuffer[i+1] = passWord[i]; + for (byte i = 0; i < 4; i++) { + cmdBuffer[i + 1] = passWord[i]; + } result = PCD_CalculateCRC(cmdBuffer, 5, &cmdBuffer[5]); - if (result!=STATUS_OK) { - return result; - } + if (result!=STATUS_OK) return result; // Transceive the data, store the reply in cmdBuffer[] byte waitIRq = 0x30; // RxIRq and IdleIRq @@ -1167,9 +1128,7 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut pACK[0] = cmdBuffer[0]; pACK[1] = cmdBuffer[1]; - if (result!=STATUS_OK) { - return result; - } + if (result!=STATUS_OK) return result; return STATUS_OK; } // End PCD_NTAG216_AUTH() @@ -1179,30 +1138,27 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut // Support functions ///////////////////////////////////////////////////////////////////////////////////// + /** * Wrapper for MIFARE protocol communication. * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. * + * @param sendData Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + * @param sendLen Number of bytes in sendData. + * @param acceptTimeout True => A timeout is also success * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. - byte sendLen, ///< Number of bytes in sendData. - bool acceptTimeout ///< True => A timeout is also success - ) { +MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive(byte* sendData, byte sendLen, bool acceptTimeout) { MFRC522::StatusCode result; byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. // Sanity check - if (sendData == nullptr || sendLen > 16) { - return STATUS_INVALID; - } + if (sendData == nullptr || sendLen > 16) return STATUS_INVALID; // Copy sendData[] to cmdBuffer[] and add CRC_A memcpy(cmdBuffer, sendData, sendLen); result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; sendLen += 2; // Transceive the data, store the reply in cmdBuffer[] @@ -1210,19 +1166,11 @@ MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointe byte cmdBufferSize = sizeof(cmdBuffer); byte validBits = 0; result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); - if (acceptTimeout && result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result != STATUS_OK) { - return result; - } + if (acceptTimeout && result == STATUS_TIMEOUT) return STATUS_OK; + if (result != STATUS_OK) return result; // The PICC must reply with a 4 bit ACK - if (cmdBufferSize != 1 || validBits != 4) { - return STATUS_ERROR; - } - if (cmdBuffer[0] != MF_ACK) { - return STATUS_MIFARE_NACK; - } + if (cmdBufferSize != 1 || validBits != 4) return STATUS_ERROR; + if (cmdBuffer[0] != MF_ACK) return STATUS_MIFARE_NACK; return STATUS_OK; } // End PCD_MIFARE_Transceive() @@ -1230,10 +1178,10 @@ MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointe /** * Translates the SAK (Select Acknowledge) to a PICC type. * + * @param sak The SAK byte returned from PICC_Select(). * @return PICC_Type */ -MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). - ) { +MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak) { // http://www.nxp.com/documents/application_note/AN10833.pdf // 3.2 Coding of Select Acknowledge (SAK) // ignore 8-bit (iso14443 starts with LSBit = bit 1) @@ -1276,19 +1224,21 @@ void MFRC522::PCD_DumpVersionToSerial() { ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); // When 0x00 or 0xFF is returned, communication probably failed - if ((v == 0x00) || (v == 0xFF)) + if ((v == 0x00) || (v == 0xFF)) { ESP_LOGD(LOG_TAG, "WARNING: Communication failure, is the MFRC522 properly connected?"); + } } // End PCD_DumpVersionToSerial() + /** * Dumps debug info about the selected PICC to Serial. * On success the PICC is halted after dumping the data. * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). * @DEPRECATED Kept for bakward compatibility */ -void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). - ) { +void MFRC522::PICC_DumpToSerial(Uid* uid) { MIFARE_Key key; // Dump UID, SAK and Type @@ -1325,17 +1275,17 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro break; // No memory dump here } - ESP_LOGD(LOG_TAG,""); PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. } // End PICC_DumpToSerial() + /** * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). * @DEPRECATED kept for backward compatibility */ -void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). - ) { +void MFRC522::PICC_DumpDetailsToSerial(Uid* uid) { // UID std::ostringstream oss; oss << std::hex << std::setfill('0'); @@ -1351,7 +1301,6 @@ void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct retur oss << "" << "Card SAK: " << std::setw(2) << (int)uid->sak; ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); - // (suggested) PICC type PICC_Type piccType = PICC_GetType(uid->sak); oss.str(""); @@ -1359,31 +1308,30 @@ void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct retur ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } // End PICC_DumpDetailsToSerial() + /** * Dumps memory contents of a MIFARE Classic PICC. * On success the PICC is halted after dumping the data. + * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). + * @param piccType One of the PICC_Type enums. + * @param key Key A used for all sectors. */ -void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - PICC_Type piccType, ///< One of the PICC_Type enums. - MIFARE_Key *key ///< Key A used for all sectors. - ) { +void MFRC522::PICC_DumpMifareClassicToSerial(Uid* uid, PICC_Type piccType, MIFARE_Key* key) { byte no_of_sectors = 0; switch (piccType) { case PICC_TYPE_MIFARE_MINI: // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. no_of_sectors = 5; break; - case PICC_TYPE_MIFARE_1K: // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. no_of_sectors = 16; break; - case PICC_TYPE_MIFARE_4K: // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. no_of_sectors = 40; break; - default: // Should not happen. Ignore. break; } @@ -1399,15 +1347,17 @@ void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid st PCD_StopCrypto1(); } // End PICC_DumpMifareClassicToSerial() + /** * Dumps memory contents of a sector of a MIFARE Classic PICC. * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. + * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). + * @param key Key A for the sector. + * @param sector The sector to dump, 0..39. */ -void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - MIFARE_Key *key, ///< Key A for the sector. - byte sector ///< The sector to dump, 0..39. - ) { +void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid* uid, MIFARE_Key* key, byte sector) { MFRC522::StatusCode status; byte firstBlock; // Address of lowest address to dump actually last block dumped) byte no_of_blocks; // Number of blocks in sector @@ -1436,8 +1386,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U else if (sector < 40) { // Sectors 32-39 has 16 blocks each no_of_blocks = 16; firstBlock = 128 + (sector - 32) * no_of_blocks; - } - else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. + } else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. return; } @@ -1452,28 +1401,23 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U // Sector number - only on first line std::ostringstream oss; if (isSectorTrailer) { - if(sector < 10) { + if (sector < 10) { oss << " "; // Pad with spaces - } - else { + } else { oss << " "; // Pad with spaces } - oss << (int)sector; - + oss << (int) sector; oss << " "; - } - else { oss << " "; + } else { } // Block number - if(blockAddr < 10) { + if (blockAddr < 10) { oss << " "; // Pad with spaces - } - else { - if(blockAddr < 100) { + } else { + if (blockAddr < 100) { oss << " "; // Pad with spaces - } - else { + } else { oss << " "; // Pad with spaces } } @@ -1502,7 +1446,6 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U oss << std::hex << std::setfill('0'); for (byte index = 0; index < 16; index++) { oss << " " << std::setw(2) << (int)buffer[index]; - if ((index % 4) == 3) { oss << " "; } @@ -1527,8 +1470,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (no_of_blocks == 4) { group = blockOffset; firstInGroup = true; - } - else { + } else { group = blockOffset / 5; firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); } @@ -1536,15 +1478,15 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (firstInGroup) { // Print access bits oss << std::dec; - oss << " [ " << (int)((g[group] >> 2) & 1) << " " << (int)((g[group] >> 1) & 1) << " " << (int) ((g[group] >> 0) & 1) << " ] "; + oss << " [ " << ((g[group] >> 2) & 1) << " " << ((g[group] >> 1) & 1) << " " << ((g[group] >> 0) & 1) << " ] "; if (invertedError) { oss << " Inverted access bits did not match! "; } } if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block - int32_t value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); - oss << " Value=0x" << std::hex << value << " Adr=0x" << (int)buffer[12]; + int32_t value = (int32_t(buffer[3]) << 24) | (int32_t(buffer[2]) << 16) | (int32_t(buffer[1]) << 8) | int32_t(buffer[0]); + oss << " Value=0x" << std::hex << value << " Adr=0x" << (int) buffer[12]; } ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } @@ -1552,6 +1494,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U return; } // End PICC_DumpMifareClassicSectorToSerial() + /** * Dumps memory contents of a MIFARE Ultralight PICC. */ @@ -1564,7 +1507,7 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { std::ostringstream oss; oss << "Page 0 1 2 3"; // Try the mpages of the original Ultralight. Ultralight C has more pages. - for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + for (byte page = 0; page < 16; page += 4) { // Read returns data for 4 pages at a time. // Read pages byteCount = sizeof(buffer); status = MIFARE_Read(page, buffer, &byteCount); @@ -1582,22 +1525,24 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { for (byte index = 0; index < 4; index++) { i = 4 * offset + index; - oss << std::hex << std::setw(2) << (int)buffer[i]; + oss << std::hex << std::setw(2) << (int) buffer[i]; } ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } } } // End PICC_DumpMifareUltralightToSerial() + /** * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1). + * + * @param accessBitBuffer Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + * @param g0 Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + * @param g1 Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + * @param g2 Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + * @param g3 Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) */ -void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. - byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) - byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) - byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) - byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) - ) { +void MFRC522::MIFARE_SetAccessBits(byte* accessBitBuffer, byte g0, byte g1, byte g2, byte g3) { byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); @@ -1611,6 +1556,7 @@ void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte // Convenience functions - does not add extra functionality ///////////////////////////////////////////////////////////////////////////////////// + /** * Returns true if a PICC responds to PICC_CMD_REQA. * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. @@ -1631,6 +1577,7 @@ bool MFRC522::PICC_IsNewCardPresent() { return (result == STATUS_OK || result == STATUS_COLLISION); } // End PICC_IsNewCardPresent() + /** * Simple wrapper around PICC_Select. * Returns true if a UID could be read. diff --git a/cpp_utils/MFRC522.h b/cpp_utils/MFRC522.h index 2e060712..9e03ba4f 100644 --- a/cpp_utils/MFRC522.h +++ b/cpp_utils/MFRC522.h @@ -76,7 +76,6 @@ #define MFRC522_h - #include #include @@ -258,7 +257,6 @@ class MFRC522 { PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. - PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. // The read/write commands can also be used for MIFARE Ultralight. @@ -329,18 +327,20 @@ class MFRC522 { ///////////////////////////////////////////////////////////////////////////////////// // Functions for setting up the Arduino ///////////////////////////////////////////////////////////////////////////////////// + //MFRC522(); ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(PCD_Register reg, byte value); - void PCD_WriteRegister(PCD_Register reg, byte count, byte *values); + void PCD_WriteRegister(PCD_Register reg, byte count, byte* values); byte PCD_ReadRegister(PCD_Register reg); - void PCD_ReadRegister(PCD_Register reg, byte count, byte *values, byte rxAlign = 0); + void PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte rxAlign = 0); void PCD_SetRegisterBitMask(PCD_Register reg, byte mask); void PCD_ClearRegisterBitMask(PCD_Register reg, byte mask); - StatusCode PCD_CalculateCRC(byte *data, byte length, byte *result); + StatusCode PCD_CalculateCRC(byte* data, byte length, byte* result); ///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 @@ -357,46 +357,46 @@ class MFRC522 { ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with PICCs ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); - StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = nullptr, byte *backLen = nullptr, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); - StatusCode PICC_RequestA(byte *bufferATQA, byte *bufferSize); - StatusCode PICC_WakeupA(byte *bufferATQA, byte *bufferSize); - StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); - virtual StatusCode PICC_Select(Uid *uid, byte validBits = 0); + StatusCode PCD_TransceiveData(byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte* sendData, byte sendLen, byte* backData = nullptr, byte* backLen = nullptr, byte* validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PICC_RequestA(byte* bufferATQA, byte* bufferSize); + StatusCode PICC_WakeupA(byte* bufferATQA, byte* bufferSize); + StatusCode PICC_REQA_or_WUPA(byte command, byte* bufferATQA, byte* bufferSize); + virtual StatusCode PICC_Select(Uid* uid, byte validBits = 0); StatusCode PICC_HaltA(); ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key* key, Uid* uid); void PCD_StopCrypto1(); - StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); - StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); - StatusCode MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + StatusCode MIFARE_Read(byte blockAddr, byte* buffer, byte* bufferSize); + StatusCode MIFARE_Write(byte blockAddr, byte* buffer, byte bufferSize); + StatusCode MIFARE_Ultralight_Write(byte page, byte* buffer, byte bufferSize); StatusCode MIFARE_Decrement(byte blockAddr, int32_t delta); StatusCode MIFARE_Increment(byte blockAddr, int32_t delta); StatusCode MIFARE_Restore(byte blockAddr); StatusCode MIFARE_Transfer(byte blockAddr); - StatusCode MIFARE_GetValue(byte blockAddr, int32_t *value); + StatusCode MIFARE_GetValue(byte blockAddr, int32_t* value); StatusCode MIFARE_SetValue(byte blockAddr, int32_t value); - StatusCode PCD_NTAG216_AUTH(byte *passWord, byte pACK[]); + StatusCode PCD_NTAG216_AUTH(byte* passWord, byte pACK[]); ///////////////////////////////////////////////////////////////////////////////////// // Support functions ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + StatusCode PCD_MIFARE_Transceive(byte* sendData, byte sendLen, bool acceptTimeout = false); static PICC_Type PICC_GetType(byte sak); // Support functions for debuging void PCD_DumpVersionToSerial(); - void PICC_DumpToSerial(Uid *uid); - void PICC_DumpDetailsToSerial(Uid *uid); - void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); - void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpToSerial(Uid* uid); + void PICC_DumpDetailsToSerial(Uid* uid); + void PICC_DumpMifareClassicToSerial(Uid* uid, PICC_Type piccType, MIFARE_Key* key); + void PICC_DumpMifareClassicSectorToSerial(Uid* uid, MIFARE_Key* key, byte sector); void PICC_DumpMifareUltralightToSerial(); // Advanced functions for MIFARE - void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + void MIFARE_SetAccessBits(byte* accessBitBuffer, byte g0, byte g1, byte g2, byte g3); ///////////////////////////////////////////////////////////////////////////////////// // Convenience functions - does not add extra functionality diff --git a/cpp_utils/MFRC522Debug.h b/cpp_utils/MFRC522Debug.h index 6c1c9ac6..e8033495 100644 --- a/cpp_utils/MFRC522Debug.h +++ b/cpp_utils/MFRC522Debug.h @@ -4,11 +4,10 @@ #define MFRC522Debug_h class MFRC522Debug { -private: - public: // Get human readable code and type static const char* PICC_GetTypeName(MFRC522::PICC_Type type); static const char* GetStatusCodeName(MFRC522::StatusCode code); + }; #endif // MFRC522Debug_h diff --git a/cpp_utils/MMU.cpp b/cpp_utils/MMU.cpp index e43328fb..57c0ccce 100644 --- a/cpp_utils/MMU.cpp +++ b/cpp_utils/MMU.cpp @@ -27,18 +27,18 @@ typedef struct { static addressRange_t entryNumberToAddressRange(uint32_t entryNumber) { addressRange_t ret; if (entryNumber < 64) { - ret.low = 0x3F400000 + 64*1024*entryNumber; - ret.high = 0x3F400000 + 64*1024*(entryNumber+1)-1; + ret.low = 0x3F400000 + 64 * 1024 * entryNumber; + ret.high = 0x3F400000 + 64 * 1024 * (entryNumber + 1) - 1; return ret; } - ret.low = 0x40000000 + 64*1024*(entryNumber-64); - ret.high = 0x40000000 + 64*1024*(entryNumber+1-64)-1; + ret.low = 0x40000000 + 64 * 1024 * (entryNumber - 64); + ret.high = 0x40000000 + 64 * 1024 * (entryNumber + 1 - 64) - 1; return ret; } static uint32_t flashPageToOffset(uint32_t page) { - return page*64*1024; + return page * 64 * 1024; } @@ -47,7 +47,7 @@ static uint32_t flashPageToOffset(uint32_t page) { const uint32_t mappingInvalid = 1 << 8; printf("PRO CPU MMU\n"); - for (int i=0; i<256; i++) { + for (uint8_t i = 0; i < 256; i++) { if (!(DPORT_PRO_FLASH_MMU_TABLE[i] & mappingInvalid)) { addressRange_t addressRange = entryNumberToAddressRange(i); printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", @@ -59,7 +59,7 @@ static uint32_t flashPageToOffset(uint32_t page) { } printf("\n"); printf("APP CPU MMU\n"); - for (int i=0; i<256; i++) { + for (uint8_t i = 0; i < 256; i++) { if (!(DPORT_APP_FLASH_MMU_TABLE[i] & mappingInvalid)) { addressRange_t addressRange = entryNumberToAddressRange(i); printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", @@ -78,17 +78,16 @@ extern "C" { static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size) { printf(">> MMU::mapFlashToVMA: flash offset: 0x%x, VMA: 0x%x, size: %d\n", flashOffset, (uint32_t)vma, size); uint32_t mmuEntryStart; // The MMU table entry to start mapping. - uint32_t mmuEntryEnd; // The MMU table entry to end mapping. + uint32_t mmuEntryEnd; // The MMU table entry to end mapping. - if ((uint32_t)vma >= 0x40000000 && (uint32_t)vma < 0x40C00000) { - mmuEntryStart = (((uint32_t)vma - 0x40000000)/(64*1024)) + 64; - mmuEntryEnd = (((uint32_t)vma - 0x40000000 + size)/(64*1024)) + 64; + if ((uint32_t) vma >= 0x40000000 && (uint32_t) vma < 0x40C00000) { + mmuEntryStart = (((uint32_t) vma - 0x40000000) / (64 * 1024)) + 64; + mmuEntryEnd = (((uint32_t) vma - 0x40000000 + size) / (64 * 1024)) + 64; } - else if ((uint32_t)vma >= 0x3F400000 && (uint32_t)vma < 0x3F800000) { - mmuEntryStart = (((uint32_t)vma - 0x3F400000)/(64*1024)); - mmuEntryEnd = (((uint32_t)vma - 0x3F400000 + size)/(64*1024)); - } - else { + else if ((uint32_t) vma >= 0x3F400000 && (uint32_t) vma < 0x3F800000) { + mmuEntryStart = (((uint32_t) vma - 0x3F400000) / (64 * 1024)); + mmuEntryEnd = (((uint32_t) vma - 0x3F400000 + size) / (64 * 1024)); + } else { printf(" - Unable to map from flash to VMA."); return; } @@ -98,14 +97,14 @@ static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, si uint32_t pFlashEnd = flashOffset + size; printf(" - Mapping flash to VMA via MMU. MMU entries start: %d, end: %d, mapping flash 0x%x (flash page: %d) to 0x%x (flash page: %d)\n", - mmuEntryStart, mmuEntryEnd, pFlashStart, pFlashStart/(64*1024), pFlashEnd, pFlashEnd/(64*1024)); + mmuEntryStart, mmuEntryEnd, pFlashStart, pFlashStart/(64 * 1024), pFlashEnd, pFlashEnd / (64 * 1024)); - uint32_t flashRegion = pFlashStart / (64*1024); // Determine the 64K chunk of flash to be mapped (we map in units of 64K). + uint32_t flashRegion = pFlashStart / (64 * 1024); // Determine the 64K chunk of flash to be mapped (we map in units of 64K). spi_flash_disable_interrupts_caches_and_other_cpu(); // For each of the mapping entries, map it to the corresponding flash region. - for (uint32_t i=mmuEntryStart; i<=mmuEntryEnd; i++) { + for (uint32_t i = mmuEntryStart; i <= mmuEntryEnd; i++) { DPORT_PRO_FLASH_MMU_TABLE[i] = flashRegion; // There are two tables. One for the PRO CPU and one for the APP CPU. DPORT_APP_FLASH_MMU_TABLE[i] = flashRegion; // Map both of them to the flash region. flashRegion++; diff --git a/cpp_utils/MPU6050.cpp b/cpp_utils/MPU6050.cpp index 5bcf5bb9..0f6931ae 100644 --- a/cpp_utils/MPU6050.cpp +++ b/cpp_utils/MPU6050.cpp @@ -9,14 +9,15 @@ #define MPU6050_GYRO_XOUT_H 0x43 #define MPU6050_PWR_MGMT_1 0x6B + /** * @brief Construct an %MPU6050 handler. */ MPU6050::MPU6050() { accel_x = accel_y = accel_z = 0; gyro_x = gyro_y = gyro_z = 0; - i2c=nullptr; - inited=false; + i2c = nullptr; + inited = false; } @@ -43,7 +44,7 @@ void MPU6050::readAccel() { uint8_t data[6]; i2c->beginTransaction(); i2c->read(data, 5, true); - i2c->read(data+5, false); + i2c->read(data + 5, false); i2c->endTransaction(); accel_x = (data[0] << 8) | data[1]; @@ -51,6 +52,7 @@ void MPU6050::readAccel() { accel_z = (data[4] << 8) | data[5]; } // readAccel + /** * @brief Read the gyroscopic values from the device. * @@ -66,7 +68,7 @@ void MPU6050::readGyro() { uint8_t data[6]; i2c->beginTransaction(); i2c->read(data, 5, true); - i2c->read(data+5, false); + i2c->read(data + 5, false); i2c->endTransaction(); gyro_x = (data[0] << 8) | data[1]; @@ -74,6 +76,7 @@ void MPU6050::readGyro() { gyro_z = (data[4] << 8) | data[5]; } // readGyro + /** * @brief Initialize the %MPU6050. * @param [in] sdaPin The %GPIO pin to use for %I2C SDA. @@ -94,5 +97,5 @@ void MPU6050::init(gpio_num_t sdaPin, gpio_num_t clkPin) { i2c->write(MPU6050_PWR_MGMT_1); i2c->write(0); i2c->endTransaction(); - inited=true; + inited = true; } diff --git a/cpp_utils/MPU6050.h b/cpp_utils/MPU6050.h index fe69193d..8207f6d6 100644 --- a/cpp_utils/MPU6050.h +++ b/cpp_utils/MPU6050.h @@ -16,61 +16,49 @@ * A call to init() must precede all other API calls. */ class MPU6050 { -private: - I2C *i2c; - short accel_x, accel_y, accel_z; - short gyro_x, gyro_y, gyro_z; - bool inited; public: MPU6050(); virtual ~MPU6050(); - /** * @brief Get the X acceleration value. */ - short getAccelX() const - { + short getAccelX() const { return accel_x; } /** * @brief Get the Y acceleration value. */ - short getAccelY() const - { + short getAccelY() const { return accel_y; } /** * @brief Get the Z acceleration value. */ - short getAccelZ() const - { + short getAccelZ() const { return accel_z; } /** * @brief Get the X gyroscopic value. */ - short getGyroX() const - { + short getGyroX() const { return gyro_x; } /** * @brief Get the Y gyroscopic value. */ - short getGyroY() const - { + short getGyroY() const { return gyro_y; } /** * @brief Get the Z gyroscopic value. */ - short getGyroZ() const - { + short getGyroZ() const { return gyro_z; } @@ -86,10 +74,16 @@ class MPU6050 { return sqrt(accel_x * accel_x + accel_y * accel_y + accel_z * accel_z); } - void init(gpio_num_t sdaPin=I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin=I2C::DEFAULT_CLK_PIN); + void init(gpio_num_t sdaPin = I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin = I2C::DEFAULT_CLK_PIN); void readAccel(); - void readGyro(); + +private: + I2C* i2c; + short accel_x, accel_y, accel_z; + short gyro_x, gyro_y, gyro_z; + bool inited; + }; #endif /* MAIN_MPU6050_H_ */ diff --git a/cpp_utils/MRFC522Debug.cpp b/cpp_utils/MRFC522Debug.cpp index 82cd031d..56ba91d0 100644 --- a/cpp_utils/MRFC522Debug.cpp +++ b/cpp_utils/MRFC522Debug.cpp @@ -1,46 +1,48 @@ #include "MFRC522Debug.h" + /** * Returns a __FlashStringHelper pointer to the PICC type name. * + * @param piccType One of the PICC_Type enums. * @return const __FlashStringHelper * */ -const char* MFRC522Debug::PICC_GetTypeName(MFRC522::PICC_Type piccType ///< One of the PICC_Type enums. -) { +const char* MFRC522Debug::PICC_GetTypeName(MFRC522::PICC_Type piccType) { switch (piccType) { - case MFRC522::PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; + case MFRC522::PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; case MFRC522::PICC_TYPE_ISO_18092: return "PICC compliant with ISO/IEC 18092 (NFC)"; - case MFRC522::PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; + case MFRC522::PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; case MFRC522::PICC_TYPE_MIFARE_1K: return "MIFARE 1KB"; case MFRC522::PICC_TYPE_MIFARE_4K: return "MIFARE 4KB"; case MFRC522::PICC_TYPE_MIFARE_UL: return "MIFARE Ultralight or Ultralight C"; - case MFRC522::PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; - case MFRC522::PICC_TYPE_MIFARE_DESFIRE: return "MIFARE DESFire"; - case MFRC522::PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; - case MFRC522::PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; + case MFRC522::PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; + case MFRC522::PICC_TYPE_MIFARE_DESFIRE: return "MIFARE DESFire"; + case MFRC522::PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; + case MFRC522::PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; case MFRC522::PICC_TYPE_UNKNOWN: - default: return "Unknown type"; + default: return "Unknown type"; } } // End PICC_GetTypeName() + /** * Returns a __FlashStringHelper pointer to a status code name. * + * @param code One of the StatusCode enums. * @return const __FlashStringHelper * */ -const char *MFRC522Debug::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums. -) { +const char *MFRC522Debug::GetStatusCodeName(MFRC522::StatusCode code) { switch (code) { - case MFRC522::STATUS_OK: return "Success."; + case MFRC522::STATUS_OK: return "Success."; case MFRC522::STATUS_ERROR: return "Error in communication."; - case MFRC522::STATUS_COLLISION: return "Collission detected."; - case MFRC522::STATUS_TIMEOUT: return "Timeout in communication."; - case MFRC522::STATUS_NO_ROOM: return "A buffer is not big enough."; - case MFRC522::STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; - case MFRC522::STATUS_INVALID: return "Invalid argument."; + case MFRC522::STATUS_COLLISION: return "Collision detected."; + case MFRC522::STATUS_TIMEOUT: return "Timeout in communication."; + case MFRC522::STATUS_NO_ROOM: return "A buffer is not big enough."; + case MFRC522::STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; + case MFRC522::STATUS_INVALID: return "Invalid argument."; case MFRC522::STATUS_CRC_WRONG: return "The CRC_A does not match."; - case MFRC522::STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; - default: return "Unknown error"; + case MFRC522::STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; + default: return "Unknown error"; } } // End GetStatusCodeName() diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index b9524dcf..5638b266 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -80,4 +80,3 @@ build_ble: install: build_ble rm -rf ${ARDUINO_LIBS}/ESP32_BLE unzip Arduino/ESP32_BLE.zip -d $(ARDUINO_LIBS) - diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp index e3be59db..002e6578 100644 --- a/cpp_utils/Memory.cpp +++ b/cpp_utils/Memory.cpp @@ -29,7 +29,7 @@ size_t Memory::m_lastHeapSize = 0; */ /* STATIC */ bool Memory::checkIntegrity() { bool rc = ::heap_caps_check_integrity_all(true); - if (rc == false && m_pRecords != nullptr) { + if (!rc && m_pRecords != nullptr) { dumpRanges(); abort(); } @@ -58,32 +58,29 @@ size_t Memory::m_lastHeapSize = 0; */ /* STATIC */ void Memory::dumpRanges() { // Each record contained in the Heap trace has the following format: - // // * uint32_t ccount – Timestamp of record. // * void* address – Address that was allocated or released. // * size_t size – Size of the block that was requested and allocated. // * void* alloced_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of allocator // * void* freed_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of releasor - // - if (m_pRecords == nullptr) { - return; - } + if (m_pRecords == nullptr) return; + esp_log_level_set("*", ESP_LOG_NONE); - size_t count = heap_trace_get_count(); + size_t count = (size_t) heap_trace_get_count(); heap_trace_record_t record; printf(">>> dumpRanges\n"); - for (size_t i=0; i + +#define OV7670_I2C_ADDR (0x21) + extern "C" { #include #include } -static const char *LOG_TAG="OV7670"; +static const char* LOG_TAG = "OV7670"; static bool getBit(uint8_t value, uint8_t bitNum) { - return (value & (1<> 3) << 3; uint8_t green = (((byte1 & 0b111) << 3) | ((byte2 & 0b11100000) >> 5)) << 2; uint8_t blue = (byte2 & 0b11111) << 3; - *pLine = (red + green + blue)/3; + *pLine = (red + green + blue) / 3; pLine++; - i+=2; + i += 2; } pLine = pLineSave; - GeneralUtils::hexDump(pLine, length/2); + GeneralUtils::hexDump(pLine, length / 2); } void OV7670::setFormat(uint8_t value) { @@ -120,7 +123,6 @@ void OV7670::setTestPattern(uint8_t value) { * */ -#define OV7670_I2C_ADDR (0x21) /* static void IRAM_ATTR isr_vsync(void* arg) { ESP_EARLY_LOGD(LOG_TAG, "VSYNC"); @@ -183,20 +185,19 @@ OV7670::~OV7670() { } -static esp_err_t camera_enable_out_clock(camera_config_t* config) -{ +static esp_err_t camera_enable_out_clock(camera_config_t* config) { ESP_LOGD(LOG_TAG, ">> camera_enable_out_clock: freq_hz=%d, pin=%d", config->xclk_freq_hz, config->pin_xclk); periph_module_enable(PERIPH_LEDC_MODULE); ledc_timer_config_t timer_conf; - timer_conf.duty_resolution = (ledc_timer_bit_t)1; timer_conf.freq_hz = config->xclk_freq_hz; + timer_conf.duty_resolution = (ledc_timer_bit_t) 1; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; timer_conf.timer_num = config->ledc_timer; esp_err_t err = ledc_timer_config(&timer_conf); if (err != ESP_OK) { - ESP_LOGE(LOG_TAG, "ledc_timer_config failed, rc=%x", err); - return err; + ESP_LOGE(LOG_TAG, "ledc_timer_config failed, rc=%x", err); + return err; } ledc_channel_config_t ch_conf; @@ -209,8 +210,8 @@ static esp_err_t camera_enable_out_clock(camera_config_t* config) err = ledc_channel_config(&ch_conf); if (err != ESP_OK) { - ESP_LOGE(LOG_TAG, "ledc_channel_config failed, rc=%x", err); - return err; + ESP_LOGE(LOG_TAG, "ledc_channel_config failed, rc=%x", err); + return err; } ESP_LOGD(LOG_TAG, "<< camera_enable_out_clock"); return ESP_OK; @@ -274,88 +275,90 @@ void OV7670::dump() { uint32_t outputFormat = getBit(com7, 2) << 1 | getBit(com7, 0); //uint32_t outputFormat = (com7 & (1<<2)) >> 1 | (com7 & (1<<0)); std::string outputFormatString; - switch(outputFormat) { - case 0b00: - outputFormatString = "YUV"; - break; - case 0b10: - outputFormatString = "RGB"; - break; - case 0b01: - outputFormatString = "Raw Bayer RGB"; - break; - case 0b11: - outputFormatString = "Process Bayer RGB"; - break; - default: - outputFormatString = "Unknown"; - break; - } - ESP_LOGD(LOG_TAG, "Output format: %s", outputFormatString.c_str()); - if (outputFormat == 0b10) { - uint8_t com15 = readRegister(OV7670_REG_COM15); - uint8_t rgbType = getBit(com15, 5) << 1 | getBit(com15,4); - char *rgbTypeString; - switch(rgbType) { + switch (outputFormat) { case 0b00: + outputFormatString = "YUV"; + break; case 0b10: - rgbTypeString = (char*)"Normal RGB Output"; + outputFormatString = "RGB"; break; case 0b01: - rgbTypeString = (char*)"RGB 565"; + outputFormatString = "Raw Bayer RGB"; break; case 0b11: - rgbTypeString = (char*)"RGB 555"; + outputFormatString = "Process Bayer RGB"; break; default: - rgbTypeString = (char*)"Unknown"; + outputFormatString = "Unknown"; break; + } + ESP_LOGD(LOG_TAG, "Output format: %s", outputFormatString.c_str()); + if (outputFormat == 0b10) { + uint8_t com15 = readRegister(OV7670_REG_COM15); + uint8_t rgbType = getBit(com15, 5) << 1 | getBit(com15, 4); + char* rgbTypeString; + switch (rgbType) { + case 0b00: + case 0b10: + rgbTypeString = (char*) "Normal RGB Output"; + break; + case 0b01: + rgbTypeString = (char*) "RGB 565"; + break; + case 0b11: + rgbTypeString = (char*) "RGB 555"; + break; + default: + rgbTypeString = (char*) "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Rgb Type: %s", rgbTypeString); } - ESP_LOGD(LOG_TAG, "Color bar: %s", getBit(com7, 1)?"Enabled":"Disabled"); + ESP_LOGD(LOG_TAG, "Color bar: %s", getBit(com7, 1) ? "Enabled" : "Disabled"); uint8_t scaling_xsc = readRegister(OV7670_REG_SCALING_XSC); uint8_t scaling_ysc = readRegister(OV7670_REG_SCALING_YSC); uint32_t testPattern = getBit(scaling_xsc, 7) << 1 | getBit(scaling_ysc, 7); - char *testPatternString; - switch(testPattern) { - case 0b00: - testPatternString = (char*)"No test output"; - break; - case 0b01: - testPatternString = (char*)"Shifting 1"; - break; - case 0b10: - testPatternString = (char*)"8-bar color bar"; - break; - case 0b11: - testPatternString = (char*)"Fade to gray color bar"; - break; - default: - testPatternString = (char*)"Unknown"; - break; + char* testPatternString; + switch (testPattern) { + case 0b00: + testPatternString = (char*) "No test output"; + break; + case 0b01: + testPatternString = (char*) "Shifting 1"; + break; + case 0b10: + testPatternString = (char*) "8-bar color bar"; + break; + case 0b11: + testPatternString = (char*) "Fade to gray color bar"; + break; + default: + testPatternString = (char*) "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Test pattern: %s", testPatternString); ESP_LOGD(LOG_TAG, "Horizontal scale factor: %d", scaling_xsc & 0x3f); ESP_LOGD(LOG_TAG, "Vertical scale factor: %d", scaling_ysc & 0x3f); uint8_t com15 = readRegister(OV7670_REG_COM15); - switch((com15 & 0b11000000) >> 6) { - case 0b00: - case 0b01: - ESP_LOGD(LOG_TAG, "Output range: 0x10 to 0xf0"); - break; - case 0b10: - ESP_LOGD(LOG_TAG, "Output range: 0x01 to 0xfe"); - break; - case 0b11: - ESP_LOGD(LOG_TAG, "Output range: 0x00 to 0xff"); - break; + switch ((com15 & 0b11000000) >> 6) { + case 0b00: + case 0b01: + ESP_LOGD(LOG_TAG, "Output range: 0x10 to 0xf0"); + break; + case 0b10: + ESP_LOGD(LOG_TAG, "Output range: 0x01 to 0xfe"); + break; + case 0b11: + ESP_LOGD(LOG_TAG, "Output range: 0x00 to 0xff"); + break; + default: + break; } } // dump /* -static void log(char *marker) { +static void log(char* marker) { ESP_LOGD(LOG_TAG, "%s", marker); FreeRTOS::sleep(100); } @@ -394,7 +397,7 @@ void OV7670::init(camera_config_t cameraConfig) { // Create the I2C interface. m_i2c = new I2C(); - m_i2c->init(OV7670_I2C_ADDR, (gpio_num_t)m_cameraConfig.pin_sscb_sda, (gpio_num_t)m_cameraConfig.pin_sscb_scl); + m_i2c->init(OV7670_I2C_ADDR, (gpio_num_t) m_cameraConfig.pin_sscb_sda, (gpio_num_t) m_cameraConfig.pin_sscb_scl); m_i2c->scan(); ESP_LOGD(LOG_TAG, "Do you see 0x21 listed?"); resetCamera(); @@ -430,46 +433,42 @@ void OV7670::init(camera_config_t cameraConfig) { } */ - I2S i2s; - dma_config_t dmaConfig; - dmaConfig.pin_d0 = cameraConfig.pin_d0; - dmaConfig.pin_d1 = cameraConfig.pin_d1; - dmaConfig.pin_d2 = cameraConfig.pin_d2; - dmaConfig.pin_d3 = cameraConfig.pin_d3; - dmaConfig.pin_d4 = cameraConfig.pin_d4; - dmaConfig.pin_d5 = cameraConfig.pin_d5; - dmaConfig.pin_d6 = cameraConfig.pin_d6; - dmaConfig.pin_d7 = cameraConfig.pin_d7; - dmaConfig.pin_href = cameraConfig.pin_href; - dmaConfig.pin_pclk = cameraConfig.pin_pclk; - dmaConfig.pin_vsync = cameraConfig.pin_vsync; - i2s.cameraMode(dmaConfig, 50, 360*2); - ESP_LOGD(LOG_TAG, "Waiting for data!"); - while(1) { - DMAData dmaData = i2s.waitForData(); - //GeneralUtils::hexDump(dmaData.getData(), dmaData.getLength()); - toGrayscale(dmaData.getData(), dmaData.getLength()); - dmaData.free(); - } - - ESP_LOGD(LOG_TAG, "Waiting for positive edge on VSYNC"); - while (gpio_get_level(m_cameraConfig.pin_vsync) == 0) { - ; - } - while (gpio_get_level(m_cameraConfig.pin_vsync) != 0) { - ; - } - - - ESP_LOGD(LOG_TAG, "Got VSYNC"); - - - - - while(1) { - FreeRTOS::sleep(1000); - ESP_LOGD(LOG_TAG, "VSYNC Counter: %d, lastHref=%d, pclk=%d", vsyncCounter, lastHref, pclkCounter); - } + I2S i2s; + dma_config_t dmaConfig; + dmaConfig.pin_d0 = cameraConfig.pin_d0; + dmaConfig.pin_d1 = cameraConfig.pin_d1; + dmaConfig.pin_d2 = cameraConfig.pin_d2; + dmaConfig.pin_d3 = cameraConfig.pin_d3; + dmaConfig.pin_d4 = cameraConfig.pin_d4; + dmaConfig.pin_d5 = cameraConfig.pin_d5; + dmaConfig.pin_d6 = cameraConfig.pin_d6; + dmaConfig.pin_d7 = cameraConfig.pin_d7; + dmaConfig.pin_href = cameraConfig.pin_href; + dmaConfig.pin_pclk = cameraConfig.pin_pclk; + dmaConfig.pin_vsync = cameraConfig.pin_vsync; + i2s.cameraMode(dmaConfig, 50, 360 * 2); + ESP_LOGD(LOG_TAG, "Waiting for data!"); + while (true) { + DMAData dmaData = i2s.waitForData(); +// GeneralUtils::hexDump(dmaData.getData(), dmaData.getLength()); + toGrayscale(dmaData.getData(), dmaData.getLength()); + dmaData.free(); + } + + ESP_LOGD(LOG_TAG, "Waiting for positive edge on VSYNC"); + while (gpio_get_level(m_cameraConfig.pin_vsync) == 0) { + ; + } + while (gpio_get_level(m_cameraConfig.pin_vsync) != 0) { + ; + } + + ESP_LOGD(LOG_TAG, "Got VSYNC"); + + while (true) { + FreeRTOS::sleep(1000); + ESP_LOGD(LOG_TAG, "VSYNC Counter: %d, lastHref=%d, pclk=%d", vsyncCounter, lastHref, pclkCounter); + } ESP_LOGD(LOG_TAG, "<< init"); } // init diff --git a/cpp_utils/OV7670.h b/cpp_utils/OV7670.h index b7baf1f9..0fb4fa19 100644 --- a/cpp_utils/OV7670.h +++ b/cpp_utils/OV7670.h @@ -198,11 +198,13 @@ class OV7670 { void setRGBFormat(uint8_t value); void setTestPattern(uint8_t value); void resetCamera(); + private: uint8_t readRegister(uint8_t reg); void writeRegister(uint8_t reg, uint8_t value); camera_config_t m_cameraConfig; - I2C *m_i2c; + I2C* m_i2c; + }; #endif /* CPP_UTILS_OV7670_H_ */ diff --git a/cpp_utils/PCF8574.cpp b/cpp_utils/PCF8574.cpp index 4d850a8b..d19222fa 100644 --- a/cpp_utils/PCF8574.cpp +++ b/cpp_utils/PCF8574.cpp @@ -22,6 +22,7 @@ PCF8574::PCF8574(uint8_t address) { lastWrite = 0; } + /** * @brief Class instance destructor. */ @@ -49,11 +50,9 @@ uint8_t PCF8574::read() { * @return True if the pin is high, false otherwise. Undefined if there is no signal on the pin. */ bool PCF8574::readBit(uint8_t bit) { - if (bit > 7) { - return false; - } + if (bit > 7) return false; uint8_t value = read(); - return (value & (1< 7) { - return; - } + if (bit > 7) return; if (invert) { value = !value; } diff --git a/cpp_utils/PCF8574.h b/cpp_utils/PCF8574.h index 15b11da9..616fdbfb 100644 --- a/cpp_utils/PCF8574.h +++ b/cpp_utils/PCF8574.h @@ -21,7 +21,7 @@ class PCF8574 { public: PCF8574(uint8_t address); virtual ~PCF8574(); - void init(gpio_num_t sdaPin=I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin=I2C::DEFAULT_CLK_PIN); + void init(gpio_num_t sdaPin = I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin = I2C::DEFAULT_CLK_PIN); uint8_t read(); bool readBit(uint8_t bit); void setInvert(bool value); @@ -32,6 +32,7 @@ class PCF8574 { I2C i2c = I2C(); uint8_t lastWrite; bool invert = false; + }; #endif /* COMPONENTS_CPP_UTILS_PCF8574_H_ */ diff --git a/cpp_utils/PCF8575.cpp b/cpp_utils/PCF8575.cpp index 3714abb1..24b15c1d 100644 --- a/cpp_utils/PCF8575.cpp +++ b/cpp_utils/PCF8575.cpp @@ -8,6 +8,7 @@ #include "PCF8575.h" #include "I2C.h" + /** * @brief Class constructor. * @@ -22,6 +23,7 @@ PCF8575::PCF8575(uint8_t address) { m_lastWrite = 0; } + /** * @brief Class instance destructor. */ @@ -36,8 +38,8 @@ PCF8575::~PCF8575() { uint16_t PCF8575::read() { uint16_t value; i2c.beginTransaction(); - i2c.read((uint8_t*)&value,true); - i2c.read(((uint8_t*)&value) + 1,true); + i2c.read((uint8_t*) &value, true); + i2c.read(((uint8_t*) &value) + 1, true); i2c.endTransaction(); return value; } // read @@ -50,11 +52,9 @@ uint16_t PCF8575::read() { * @return True if the pin is high, false otherwise. Undefined if there is no signal on the pin. */ bool PCF8575::readBit(uint16_t bit) { - if (bit > 7) { - return false; - } + if (bit > 7) return false; uint16_t value = read(); - return (value & (1< 15) { - return; - } + if (bit > 15) return; if (invert) { value = !value; } if (value) { - m_lastWrite |= (1< 100) { - percent = 100; - } + if (percent > 100) percent = 100; uint32_t value = max * percent / 100; - if (value >= max) { - value = max-1; - } + if (value >= max) value = max - 1; setDuty(value); - } // setDutyPercentage diff --git a/cpp_utils/PWM.h b/cpp_utils/PWM.h index 01c30a48..8cc1ce39 100644 --- a/cpp_utils/PWM.h +++ b/cpp_utils/PWM.h @@ -32,11 +32,13 @@ class PWM { void setDuty(uint32_t duty); void setDutyPercentage(uint8_t percent); void setFrequency(uint32_t freq); - void stop(bool idleLevel=false); + void stop(bool idleLevel = false); + private: ledc_channel_t m_channel; ledc_timer_t m_timer; ledc_timer_bit_t m_dutyResolution; // Bit size of timer. + }; #endif /* COMPONENTS_CPP_UTILS_PWM_H_ */ diff --git a/cpp_utils/PubSubClient.cpp b/cpp_utils/PubSubClient.cpp index 8aaca5eb..30cb6282 100644 --- a/cpp_utils/PubSubClient.cpp +++ b/cpp_utils/PubSubClient.cpp @@ -30,6 +30,7 @@ class PubSubClientTask: public Task { Task(name, 16 * 1024) { taskName = name; }; + private: std::string taskName; /** @@ -40,30 +41,24 @@ class PubSubClientTask: public Task { PubSubClient* pPubSubClient = (PubSubClient*) data; ESP_LOGD("PubSubClientTask", "PubSubClientTask Task started!"); - while(1) { + while (true) { if (pPubSubClient->connected()) { - uint16_t len = pPubSubClient->readPacket(); if (len > 0) { // if there was data pPubSubClient->keepAliveTimer->reset(0); //lastInActivity = t; - mqtt_message *msg = new mqtt_message; + mqtt_message* msg = new mqtt_message; pPubSubClient->parseData(msg, len); //pPubSubClient->dumpData(msg); ESP_LOGD(TAG, "Message type (%s)!", pPubSubClient->messageType_toString(msg->type).c_str()); - if (msg->type == PUBLISH) { - if (pPubSubClient->callback) { - if (msg->qos == QOS0) { - pPubSubClient->callback(msg->topic, msg->payload); - } else if (msg->qos == QOS1) { pPubSubClient->callback(msg->topic, msg->payload); @@ -73,31 +68,27 @@ class PubSubClientTask: public Task { pPubSubClient->buffer[3] = (msg->msgId & 0xFF); int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 4); - if(rc < 0) pPubSubClient->_state = CONNECTION_LOST; + if (rc < 0) pPubSubClient->_state = CONNECTION_LOST; pPubSubClient->keepAliveTimer->reset(0); //lastOutActivity = t; - - }else if(msg->qos == QOS2) { + } else if(msg->qos == QOS2) { ESP_LOGD(TAG, "QOS2 is not supported!"); - }else{ + } else { ESP_LOGD(TAG, "QOS-Level unkonwon yet!"); } - } } else if (msg->type == PINGREQ) { pPubSubClient->buffer[0] = PINGRESP; pPubSubClient->buffer[1] = 0; int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 2); - if(rc < 0) pPubSubClient->_state = CONNECTION_LOST; + if (rc < 0) pPubSubClient->_state = CONNECTION_LOST; } else if (msg->type == PINGRESP) { pPubSubClient->PING_outstanding = false; - - }else if (msg->type == SUBACK) { + } else if (msg->type == SUBACK) { pPubSubClient->SUBACK_outstanding = false; pPubSubClient->timeoutTimer->stop(0); - - }else if (msg->type == UNSUBACK) { + } else if (msg->type == UNSUBACK) { pPubSubClient->UNSUBACK_Outstanding = false; pPubSubClient->timeoutTimer->stop(0); } @@ -105,10 +96,11 @@ class PubSubClientTask: public Task { delete(msg); } } - } // While (1) + } // while (true) } // run }; // PubSubClientTask + PubSubClient::PubSubClient() { setup(); this->_state = DISCONNECTED; @@ -116,12 +108,14 @@ PubSubClient::PubSubClient() { setCallback(NULL); } + PubSubClient::PubSubClient(Socket& client) { setup(); this->_state = DISCONNECTED; setClient(client); } + PubSubClient::PubSubClient(std::string addr, uint16_t port) { setup(); this->_state = DISCONNECTED; @@ -129,6 +123,7 @@ PubSubClient::PubSubClient(std::string addr, uint16_t port) { setServer(addr, port); } + PubSubClient::PubSubClient(std::string addr, uint16_t port, Socket& client) { setup(); this->_state = DISCONNECTED; @@ -136,8 +131,8 @@ PubSubClient::PubSubClient(std::string addr, uint16_t port, Socket& client) { setClient(client); } -PubSubClient::PubSubClient(std::string addr, uint16_t port, - MQTT_CALLBACK_SIGNATURE, Socket& client) { + +PubSubClient::PubSubClient(std::string addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Socket& client) { setup(); this->_state = DISCONNECTED; setServer(addr, port); @@ -145,6 +140,7 @@ PubSubClient::PubSubClient(std::string addr, uint16_t port, setClient(client); } + PubSubClient::~PubSubClient() { _client->close(); keepAliveTimer->stop(0); @@ -156,39 +152,41 @@ PubSubClient::~PubSubClient() { delete (m_task); } + /** * @brief This is a Timer called routine mapping routine, which calls * the PubSubClient member function keepAliveChecker. * @param The FreeRTOSTimer root instance for this callback function. * @return N/A. */ -void keepAliveTimerMapper(FreeRTOSTimer *pTimer) { +void keepAliveTimerMapper(FreeRTOSTimer* pTimer) { PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); m_pubSubClient->keepAliveChecker(); } //keepAliveChecker + /** * @brief This is a Timer called routine mapping routine, which calls * the PubSubClient member function timeoutChecker. * @param The FreeRTOSTimer root instance for this callback function. * @return N/A. */ -void timeoutTimerMapper(FreeRTOSTimer *pTimer) { +void timeoutTimerMapper(FreeRTOSTimer* pTimer) { PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); m_pubSubClient->timeoutChecker(); } //keepAliveChecker + /** * @brief This is a internal setup routine for the PubSubClient. * @param N/A. * @return N/A. */ -void PubSubClient::setup(void) { +void PubSubClient::setup() { PING_outstanding = false; SUBACK_outstanding = false; UNSUBACK_Outstanding = false; - keepAliveTimer = new FreeRTOSTimer((char*) "keepAliveTimer", (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, true, this, keepAliveTimerMapper); @@ -208,8 +206,8 @@ void PubSubClient::setup(void) { * @param N/A. * @return N/A. */ -void PubSubClient::keepAliveChecker(void){ +void PubSubClient::keepAliveChecker() { if (PING_outstanding && connected()) { _state = CONNECTION_TIMEOUT; //_client->close(); @@ -218,7 +216,7 @@ void PubSubClient::keepAliveChecker(void){ buffer[0] = PINGREQ; buffer[1] = 0; int rc = _client->send(buffer, 2); - if(rc < 0) _state = CONNECTION_LOST; + if (rc < 0) _state = CONNECTION_LOST; ESP_LOGD(TAG, "send KeepAlive REQUEST!"); PING_outstanding = true; } @@ -232,8 +230,7 @@ void PubSubClient::keepAliveChecker(void){ * @param N/A. * @return N/A. */ -void PubSubClient::timeoutChecker(void){ - +void PubSubClient::timeoutChecker() { if (connected() && (SUBACK_outstanding || UNSUBACK_Outstanding)) { _state = CONNECTION_TIMEOUT; //_client->close(); @@ -241,15 +238,17 @@ void PubSubClient::timeoutChecker(void){ } } //keepAliveChecker + /** * @brief Connect to a MQTT server. * @param [in] Device id to identify this device. * @return success (true), or no success (false). */ -bool PubSubClient::connect(const char *id) { +bool PubSubClient::connect(const char* id) { return connect(id, NULL, NULL, 0, 0, 0, 0); } + /** * @brief Connect to a MQTT server. * @param [in] Device id to identify this device. @@ -257,10 +256,11 @@ bool PubSubClient::connect(const char *id) { * [in] my password. * @return success (true), or no success (false). */ -bool PubSubClient::connect(const char *id, const char *user, const char *pass) { +bool PubSubClient::connect(const char* id, const char* user, const char* pass) { return connect(id, user, pass, 0, 0, 0, 0); } + /** * @brief Connect to a MQTT server. * @param [in] Device id to identify this device. @@ -270,11 +270,11 @@ bool PubSubClient::connect(const char *id, const char *user, const char *pass) { * [in] last will: payload. * @return success (true), or no success (false). */ -bool PubSubClient::connect(const char *id, const char* willTopic, - uint8_t willQos, bool willRetain, const char* willMessage) { +bool PubSubClient::connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage) { return connect(id, NULL, NULL, willTopic, willQos, willRetain, willMessage); } + /** * @brief Connect to a MQTT server. * @param [in] Device id to identify this device. @@ -286,9 +286,7 @@ bool PubSubClient::connect(const char *id, const char* willTopic, * [in] last will: payload. * @return success (true), or no success (false). */ -bool PubSubClient::connect(const char *id, const char *user, const char *pass, - const char* willTopic, uint8_t willQos, bool willRetain, - const char* willMessage) { +bool PubSubClient::connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage) { _config.id = id; _config.user = user; @@ -302,6 +300,7 @@ bool PubSubClient::connect(const char *id, const char *user, const char *pass, return connect(); } + /** * @brief Connect to a MQTT server with the with the previous settings. * Note: do not call this function without settings, this will not work! @@ -309,30 +308,25 @@ bool PubSubClient::connect(const char *id, const char *user, const char *pass, * @param N/A * @return success (true), or no success (false). */ -bool PubSubClient::connect(){ - +bool PubSubClient::connect() { if (!connected()) { - ESP_LOGD(TAG, "Connect to mqtt server..."); - ESP_LOGD(TAG, "ip: %s port: %d", _config.ip.c_str(), _config.port); int result = _client->connect((char *)_config.ip.c_str(), _config.port); if (result == 0) { - nextMsgId = 1; // Leave room in the buffer for header and variable length field uint16_t length = 5; - unsigned int j; #if MQTT_VERSION == MQTT_VERSION_3_1 - uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION}; + uint8_t d[9] = { 0x00, 0x06, 'M', 'Q', 'I', 's', 'd', 'p', MQTT_VERSION }; #define MQTT_HEADER_VERSION_LENGTH 9 #elif MQTT_VERSION == MQTT_VERSION_3_1_1 uint8_t d[7] = { 0x00, 0x04, 'M', 'Q', 'T', 'T', MQTT_VERSION }; #define MQTT_HEADER_VERSION_LENGTH 7 #endif - for (j = 0; j < MQTT_HEADER_VERSION_LENGTH; j++) { + for (unsigned int j = 0; j < MQTT_HEADER_VERSION_LENGTH; j++) { buffer[length++] = d[j]; } @@ -399,6 +393,7 @@ bool PubSubClient::connect(){ return true; } + /** * @brief Receiving a MQTT packet and store it in the buffer to parse it. * @param N/A. @@ -412,9 +407,10 @@ uint16_t PubSubClient::readPacket() { res = 0; // This will cause the packet to be ignored. } - return res; + return (uint16_t) res; } + /** * @brief Publish a MQTT message. * @param [in] my topic. @@ -425,6 +421,7 @@ bool PubSubClient::publish(const char* topic, const char* payload) { return publish(topic, (const uint8_t*) payload, strlen(payload), false); } + /** * @brief Publish a MQTT message. * @param [in] my topic. @@ -432,11 +429,11 @@ bool PubSubClient::publish(const char* topic, const char* payload) { * [in] is this a retained message (true/false) * @return success (true), or no success (false). */ -bool PubSubClient::publish(const char* topic, const char* payload, - bool retained) { +bool PubSubClient::publish(const char* topic, const char* payload, bool retained) { return publish(topic, (const uint8_t*) payload, strlen(payload), retained); } + /** * @brief Publish a MQTT message. * @param [in] my topic. @@ -444,11 +441,11 @@ bool PubSubClient::publish(const char* topic, const char* payload, * [in] length of the message * @return success (true), or no success (false). */ -bool PubSubClient::publish(const char* topic, const uint8_t* payload, - unsigned int plength) { +bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { return publish(topic, payload, plength, false); } + /** * @brief Publish a MQTT message. * @param [in] my topic. @@ -457,8 +454,7 @@ bool PubSubClient::publish(const char* topic, const uint8_t* payload, * [in] is this a retained message (true/false) * @return success (true), or no success (false). */ -bool PubSubClient::publish(const char* topic, const uint8_t* payload, - unsigned int plength, bool retained) { +bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, bool retained) { if (connected()) { if (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength) { // Too long @@ -480,6 +476,7 @@ bool PubSubClient::publish(const char* topic, const uint8_t* payload, return false; } + //bool PubSubClient::publish_P(const char* topic, const uint8_t* payload, // unsigned int plength, bool retained) { // uint8_t llen = 0; @@ -527,6 +524,7 @@ bool PubSubClient::publish(const char* topic, const uint8_t* payload, // return rc == tlen + 4 + plength; //} + /** * @brief Send a MQTT message over socket. * @param [in] MQTT header. @@ -557,13 +555,13 @@ bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { } //#ifdef MQTT_MAX_TRANSFER_SIZE -// uint8_t* writeBuf = buf+(4-llen); -// uint16_t bytesRemaining = length+1+llen; //Match the length type +// uint8_t* writeBuf = buf + (4 - llen); +// uint16_t bytesRemaining = length + 1 + llen; //Match the length type // uint8_t bytesToWrite; // bool result = true; -// while((bytesRemaining > 0) && result) { -// bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; -// rc = _client->write(writeBuf,bytesToWrite); +// while ((bytesRemaining > 0) && result) { +// bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE) ? MQTT_MAX_TRANSFER_SIZE : bytesRemaining; +// rc = _client->write(writeBuf, bytesToWrite); // result = (rc == bytesToWrite); // bytesRemaining -= rc; // writeBuf += rc; @@ -577,6 +575,7 @@ bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { //#endif } + /** * @brief Subscribe a MQTT topic. * @param [in] my topic @@ -584,11 +583,8 @@ bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { * @return request transmitted with success (true), or no success (false). */ bool PubSubClient::subscribe(const char* topic, bool ack) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) return false; // Too long - if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { - // Too long - return false; - } if (connected()) { // Leave room in the buffer for header and variable length field uint16_t length = 5; @@ -601,36 +597,35 @@ bool PubSubClient::subscribe(const char* topic, bool ack) { length = writeString(topic, buffer, length); buffer[length++] = QOS1; - if(write(SUBSCRIBE | QOS1, buffer, length - 5)){ + if (write(SUBSCRIBE | QOS1, buffer, length - 5)) { SUBACK_outstanding = true; - if(ack) timeoutTimer->start(0); + if (ack) timeoutTimer->start(0); return true; } } return false; } + /** * @brief Check the state of subscription. If there was received a subscription * ACK, we return a true here. * @return Is subscription validated with ACK (true/false) */ -bool PubSubClient::isSubscribeDone(void){ +bool PubSubClient::isSubscribeDone() { return !SUBACK_outstanding; } + /** * @brief Unsubscribe a MQTT topic. * @param [in] my topic * [in] qos of unsubscription * @return request transmitted with success (true), or no success (false). */ -bool PubSubClient::unsubscribe(const char* topic, bool ack) { +bool PubSubClient::unsubscribe(const char* topic, bool ack) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) return false; // Too long - if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { - // Too long - return false; - } if (connected()) { uint16_t length = 5; nextMsgId++; @@ -641,24 +636,26 @@ bool PubSubClient::unsubscribe(const char* topic, bool ack) { buffer[length++] = (nextMsgId & 0xFF); length = writeString(topic, buffer, length); - if(write(UNSUBSCRIBE | QOS1, buffer, length - 5)){ + if (write(UNSUBSCRIBE | QOS1, buffer, length - 5)) { UNSUBACK_Outstanding = true; - if(ack) timeoutTimer->start(0); + if (ack) timeoutTimer->start(0); return true; } } return false; } + /** * @brief Check the state of unsubscription. If there was received a unsubscription * ACK, we return a true here. * @return Is unsubscription validated with ACK (true/false) */ -bool PubSubClient::isUnsubscribeDone(void){ +bool PubSubClient::isUnsubscribeDone() { return !UNSUBACK_Outstanding; } + /** * @brief Disconnect form MQTT server and close the socket. * @return N/A. @@ -673,11 +670,11 @@ void PubSubClient::disconnect() { timeoutTimer->stop(0); } + /** * @brief calculation help to send a string. */ -uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, - uint16_t pos) { +uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) { const char* idp = string; uint16_t i = 0; pos += 2; @@ -690,34 +687,30 @@ uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, return pos; } + /** * @brief Check the connection to the MQTT server. * @return connected (true/false) */ bool PubSubClient::connected() { + if (this->_state != CONNECTED) return false; + bool rc = true; - if (this->_state == CONNECTED) { - - bool rc = true; - - if (_client == nullptr) { - rc = false; - } else if (!_client->isValid()) { - rc = false; + if (_client == nullptr) { + rc = false; + } else if (!_client->isValid()) { + rc = false; - this->_state = CONNECTION_LOST; + this->_state = CONNECTION_LOST; - if (_client->isValid()) _client->close(); - keepAliveTimer->stop(0); - timeoutTimer->stop(0); - } - return rc; + if (_client->isValid()) _client->close(); + keepAliveTimer->stop(0); + timeoutTimer->stop(0); } - return false; - ESP_LOGD(TAG, "_state = %d", _state); - + return rc; } + /** * @brief Set server ip and Port of my MQTT server. * @param [in] ip of the distant MQTT server. @@ -730,6 +723,7 @@ PubSubClient& PubSubClient::setServer(std::string ip, uint16_t port) { return *this; } + /** * @brief Set the callback function for incoming data. * @param [in] callback function @@ -740,6 +734,7 @@ PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) { return *this; } + /** * @brief Set the socket, which we want to use for our MQTT communication. * @param [in] the new socket instance @@ -750,6 +745,7 @@ PubSubClient& PubSubClient::setClient(Socket& client) { return *this; } + /** * @brief Get the current MYTT state form the instance. * @param N/A. @@ -759,25 +755,28 @@ int PubSubClient::state() { return this->_state; } + /** * @brief Parsing the received data in to the internal message struct. */ -void PubSubClient::parseData(mqtt_message* msg, uint16_t len){ - +void PubSubClient::parseData(mqtt_message* msg, uint16_t len) { /********* Parse Fixed header *********/ msg->type = buffer[0] & 0xF0; /* read DUP-Flag */ - if(msg->type == PUBLISH) - msg->dup = (bool)(buffer[0] & 0x18)>>3; + if (msg->type == PUBLISH) { + msg->dup = (bool) (buffer[0] & 0x18) >> 3; + } /* read QoS-Level */ - if(msg->type == PUBLISH) + if(msg->type == PUBLISH) { msg->qos = (buffer[0] & 0x06); + } /* read RETAIN-Frag */ - if(msg->type == PUBLISH) - msg->retained = (bool)(buffer[0] & 0x01); + if(msg->type == PUBLISH) { + msg->retained = (bool) (buffer[0] & 0x01); + } uint8_t remainingLength = buffer[1]; @@ -785,36 +784,36 @@ void PubSubClient::parseData(mqtt_message* msg, uint16_t len){ int pos = 2; /* read topic name */ - if(msg->type == PUBLISH){ + if (msg->type == PUBLISH) { uint16_t topicLen = (buffer[2] << 8) + buffer[3]; msg->topic = ""; - for(int i = 4; i<(topicLen+4); i++){ + for (int i = 4; i < (topicLen + 4); i++) { msg->topic += (char) buffer[i]; pos++; } } /* read Message ID */ - if(msg->type == PUBLISH || msg->type == PUBACK || msg->type == PUBREC || msg->type == PUBCOMP || msg->type == SUBACK || msg->type == UNSUBACK){ - msg->msgId = (buffer[pos]<<8) + (buffer[pos+1]); + if (msg->type == PUBLISH || msg->type == PUBACK || msg->type == PUBREC || msg->type == PUBCOMP || msg->type == SUBACK || msg->type == UNSUBACK) { + msg->msgId = (buffer[pos] << 8) + (buffer[pos + 1]); pos += 2; } /********* read Payload *********/ - if(msg->type == PUBLISH){ + if (msg->type == PUBLISH) { msg->payload = ""; - for(int i = pos; i payload += (char)buffer[i]; + for (int i = pos; i < remainingLength + 2; i++) { + msg->payload += (char) buffer[i]; } } } + /** * @brief Dump the message struct. */ -void PubSubClient::dumpData(mqtt_message* msg){ - +void PubSubClient::dumpData(mqtt_message* msg) { ESP_LOGD(TAG, "mqtt_message_type: %s", messageType_toString(msg->type).c_str()); ESP_LOGD(TAG, "mqtt_qos: %d", msg->qos); ESP_LOGD(TAG, "retained: %d", msg->retained); @@ -824,30 +823,31 @@ void PubSubClient::dumpData(mqtt_message* msg){ ESP_LOGD(TAG, "msgId: %d", msg->msgId); } + /** * @brief Convert the MQTT message type to string. * @param [in] message type byte. * @return message type as std::string. */ -std::string PubSubClient::messageType_toString(uint8_t type){ +std::string PubSubClient::messageType_toString(uint8_t type) { std::string str = "Not in list!"; - switch(type){ - case CONNECT : str = "CONNECT"; break; - case CONNACK : str = "CONNACK"; break; - case PUBLISH : str = "PUBLISH"; break; - case PUBACK : str = "PUBACK"; break; - case PUBREC : str = "PUBREC"; break; - case PUBREL : str = "PUBREL"; break; - case PUBCOMP : str = "PUBCOMP"; break; - case SUBSCRIBE : str = "SUBSCRIBE"; break; - case SUBACK : str = "SUBACK"; break; - case UNSUBSCRIBE: str = "UNSUBSCRIBE"; break; - case UNSUBACK : str = "UNSUBACK"; break; - case PINGREQ : str = "PINGREQ"; break; - case PINGRESP : str = "PINGRESP"; break; - case DISCONNECT : str = "DISCONNECT"; break; - case Reserved : str = "Reserved"; break; + switch (type) { + case CONNECT : str = "CONNECT"; break; + case CONNACK : str = "CONNACK"; break; + case PUBLISH : str = "PUBLISH"; break; + case PUBACK : str = "PUBACK"; break; + case PUBREC : str = "PUBREC"; break; + case PUBREL : str = "PUBREL"; break; + case PUBCOMP : str = "PUBCOMP"; break; + case SUBSCRIBE : str = "SUBSCRIBE"; break; + case SUBACK : str = "SUBACK"; break; + case UNSUBSCRIBE: str = "UNSUBSCRIBE"; break; + case UNSUBACK : str = "UNSUBACK"; break; + case PINGREQ : str = "PINGREQ"; break; + case PINGRESP : str = "PINGRESP"; break; + case DISCONNECT : str = "DISCONNECT"; break; + case Reserved : str = "Reserved"; break; + default : break; } - return str; } diff --git a/cpp_utils/PubSubClient.h b/cpp_utils/PubSubClient.h index 8e56ca59..c5da6ca7 100644 --- a/cpp_utils/PubSubClient.h +++ b/cpp_utils/PubSubClient.h @@ -51,14 +51,14 @@ struct mqtt_InitTypeDef{ const char* user; const char* pass; - const char * id; + const char* id; const char* willTopic; uint8_t willQos; bool willRetain; const char* willMessage; }; -typedef enum{ +typedef enum { CONNECTION_TIMEOUT = -4, CONNECTION_LOST = -3, CONNECT_FAILED = -2, @@ -69,9 +69,9 @@ typedef enum{ CONNECT_UNAVAILABLE = 3, CONNECT_BAD_CREDENTIALS = 4, CONNECT_UNAUTHORIZED = 5, -}mqtt_state; +} mqtt_state; -typedef enum{ +typedef enum { CONNECT = 1 << 4, // Client request to connect to Server CONNACK = 2 << 4, // Connect Acknowledgment PUBLISH = 3 << 4, // Publish message @@ -87,15 +87,15 @@ typedef enum{ PINGRESP = 13 << 4, // PING Response DISCONNECT = 14 << 4, // Client is Disconnecting Reserved = 15 << 4, // Reserved -}mqtt_message_type; +} mqtt_message_type; -typedef enum{ +typedef enum { QOS0 = (0 << 1), QOS1 = (1 << 1), QOS2 = (2 << 1), -}mqtt_qos; +} mqtt_qos; -struct mqtt_message{ +struct mqtt_message { uint8_t type; uint8_t qos; bool retained; @@ -105,7 +105,7 @@ struct mqtt_message{ uint16_t msgId; }; -#define MQTT_CALLBACK_SIGNATURE void (*callback)(std::string, std::string) +#define MQTT_CALLBACK_SIGNATURE void (*callback) (std::string, std::string) class PubSubClientTask; @@ -130,19 +130,19 @@ class PubSubClient { void disconnect(); bool publish(const char* topic, const char* payload); bool publish(const char* topic, const char* payload, bool retained); - bool publish(const char* topic, const uint8_t * payload, unsigned int plength); - bool publish(const char* topic, const uint8_t * payload, unsigned int plength, bool retained); + bool publish(const char* topic, const uint8_t* payload, unsigned int plength); + bool publish(const char* topic, const uint8_t* payload, unsigned int plength, bool retained); //bool publish_P(const char* topic, const uint8_t * payload, unsigned int plength, bool retained); - bool subscribe (const char* topic, bool ack=false); - bool unsubscribe (const char* topic, bool ack=false); - bool isSubscribeDone (void); - bool isUnsubscribeDone (void); + bool subscribe(const char* topic, bool ack = false); + bool unsubscribe(const char* topic, bool ack = false); + bool isSubscribeDone(); + bool isUnsubscribeDone(); - bool connected (void); - int state (void); - void keepAliveChecker (void); - void timeoutChecker (void); + bool connected(); + int state(); + void keepAliveChecker(); + void timeoutChecker(); private: friend class PubSubClientTask; @@ -159,12 +159,12 @@ class PubSubClient { FreeRTOSTimer* timeoutTimer; MQTT_CALLBACK_SIGNATURE; - void setup (void); - uint16_t readPacket(); - bool write (uint8_t header, uint8_t* buf, uint16_t length); + void setup(); + uint16_t readPacket(); + bool write(uint8_t header, uint8_t* buf, uint16_t length); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); - void parseData (mqtt_message* msg, uint16_t len); - void dumpData (mqtt_message* msg); + void parseData(mqtt_message* msg, uint16_t len); + void dumpData(mqtt_message* msg); std::string messageType_toString(uint8_t type); }; diff --git a/cpp_utils/RESTClient.cpp b/cpp_utils/RESTClient.cpp index 7160dca6..a72f80b5 100644 --- a/cpp_utils/RESTClient.cpp +++ b/cpp_utils/RESTClient.cpp @@ -17,7 +17,7 @@ #include "RESTClient.h" -static char tag[] = "RESTClient"; +static const char* LOG_TAG = "RESTClient"; RESTClient::RESTClient() { @@ -37,15 +37,15 @@ RESTClient::~RESTClient() { * @brief Perform an HTTP GET request. */ long RESTClient::get() { - long response_code; // Added return response_code 2018_4_12 + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_HTTPGET, 1); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { - ESP_LOGE(tag, "get(): %s", getErrorMessage().c_str()); + ESP_LOGE(LOG_TAG, "get(): %s", getErrorMessage().c_str()); } - curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code); // Added return response_code 2018_4_12 - return response_code; // Added return response_code 2018_4_12 + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code); // Added return response_code 2018_4_12 + return response_code; // Added return response_code 2018_4_12 } // get @@ -56,15 +56,15 @@ long RESTClient::get() { * */ long RESTClient::post(std::string body) { - long response_code; // Added return response_code 2018_4_12 + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_POSTFIELDS, body.c_str()); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { - ESP_LOGE(tag, "post(): %s", getErrorMessage().c_str()); + ESP_LOGE(LOG_TAG, "post(): %s", getErrorMessage().c_str()); } - curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code);// Added return response_code 2018_4_12 - return response_code;// Added return response_code 2018_4_12 + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code);// Added return response_code 2018_4_12 + return response_code;// Added return response_code 2018_4_12 } // post @@ -90,10 +90,10 @@ std::string RESTClient::getErrorMessage() { * * @return The number of bytes of data processed. */ -size_t RESTClient::handleData(void *buffer, size_t size, size_t nmemb, void *userp) { +size_t RESTClient::handleData(void* buffer, size_t size, size_t nmemb, void* userp) { //printf("handleData: size: %d, num: %d\n", size, nmemb); - RESTClient *pClient = (RESTClient *)userp; - pClient->m_response.append((const char *)buffer, size*nmemb); + RESTClient* pClient = (RESTClient*) userp; + pClient->m_response.append((const char*) buffer, size * nmemb); return size * nmemb; } // handleData @@ -139,7 +139,7 @@ void RESTClient::prepForCall() { } // prepForCall -RESTTimings::RESTTimings(RESTClient *client) { +RESTTimings::RESTTimings(RESTClient* client) { this->client = client; } diff --git a/cpp_utils/RESTClient.h b/cpp_utils/RESTClient.h index 4ad91d5a..f44612ac 100644 --- a/cpp_utils/RESTClient.h +++ b/cpp_utils/RESTClient.h @@ -19,9 +19,10 @@ class RESTClient; */ class RESTTimings { public: - RESTTimings(RESTClient *client); + RESTTimings(RESTClient* client); void refresh(); std::string toString(); + private: double m_namelookup = 0; double m_connect = 0; @@ -29,7 +30,8 @@ class RESTTimings { double m_pretransfer = 0; double m_starttransfer = 0; double m_total = 0; - RESTClient *client = nullptr; + RESTClient* client = nullptr; + }; /** @@ -107,19 +109,18 @@ class RESTClient { m_verbose = value; }; - - private: - CURL *m_curlHandle; + CURL* m_curlHandle; std::string m_url; char m_errbuf[CURL_ERROR_SIZE]; - struct curl_slist *m_headers = nullptr; + struct curl_slist* m_headers = nullptr; bool m_verbose = false; friend class RESTTimings; - RESTTimings *m_timings; + RESTTimings* m_timings; std::string m_response; - static size_t handleData(void *buffer, size_t size, size_t nmemb, void *userp); + static size_t handleData(void* buffer, size_t size, size_t nmemb, void* userp); void prepForCall(); + }; #endif /* CONFIG_LIBCURL_PRESENT */ #endif /* MAIN_RESTCLIENT_H_ */ diff --git a/cpp_utils/RMT.cpp b/cpp_utils/RMT.cpp index 750da1a6..9f9a5950 100644 --- a/cpp_utils/RMT.cpp +++ b/cpp_utils/RMT.cpp @@ -8,6 +8,7 @@ #include #include "RMT.h" + //static char tag[] = "RMT"; /** * @brief Create a class instance. @@ -22,7 +23,7 @@ RMT::RMT(gpio_num_t pin, rmt_channel_t channel) { config.rmt_mode = RMT_MODE_TX; config.channel = channel; config.gpio_num = pin; - config.mem_block_num = 8-this->channel; + config.mem_block_num = 8 - this->channel; config.clk_div = 8; config.tx_config.loop_en = 0; config.tx_config.carrier_en = 0; @@ -32,7 +33,6 @@ RMT::RMT(gpio_num_t pin, rmt_channel_t channel) { config.tx_config.carrier_level = (rmt_carrier_level_t)1; config.tx_config.carrier_duty_percent = 50; - ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(this->channel, 0, 0)); } @@ -45,6 +45,7 @@ RMT::~RMT() { ESP_ERROR_CHECK(::rmt_driver_uninstall(this->channel)); } + /** * @brief Start receiving. */ @@ -52,6 +53,7 @@ void RMT::rxStart() { ESP_ERROR_CHECK(::rmt_rx_start(this->channel, true)); } + /** * @brief Stop receiving. */ @@ -59,6 +61,7 @@ void RMT::rxStop() { ESP_ERROR_CHECK(::rmt_rx_stop(this->channel)); } + /** * @brief Start transmitting. */ @@ -66,6 +69,7 @@ void RMT::txStart() { ESP_ERROR_CHECK(::rmt_tx_start(this->channel, true)); } + /** * @brief Stop transmitting. */ @@ -73,6 +77,7 @@ void RMT::txStop() { ESP_ERROR_CHECK(::rmt_tx_stop(this->channel)); } + /** * @brief Write the items out through the RMT. * @@ -81,7 +86,7 @@ void RMT::txStop() { * is cleared. */ void RMT::write() { - add(false,0); + add(false, 0); ESP_ERROR_CHECK(::rmt_write_items(this->channel, &items[0], items.size(), true)); clear(); } @@ -100,12 +105,13 @@ void RMT::add(bool level, uint32_t duration) { item.duration0 = duration; items.push_back(item); } else { - items.at(bitCount/2).level1 = level; - items.at(bitCount/2).duration1 = duration; + items.at(bitCount / 2).level1 = level; + items.at(bitCount / 2).duration1 = duration; } bitCount++; } + /** * @brief Clear any previously written level/duration pairs that have not been sent. */ diff --git a/cpp_utils/RMT.h b/cpp_utils/RMT.h index 90e59007..8c079f80 100644 --- a/cpp_utils/RMT.h +++ b/cpp_utils/RMT.h @@ -15,7 +15,7 @@ */ class RMT { public: - RMT(gpio_num_t pin, rmt_channel_t channel=RMT_CHANNEL_0); + RMT(gpio_num_t pin, rmt_channel_t channel = RMT_CHANNEL_0); virtual ~RMT(); void add(bool level, uint32_t duration); void clear(); @@ -25,11 +25,11 @@ class RMT { void txStop(); void write(); - private: rmt_channel_t channel; std::vector items; int bitCount = 0; + }; #endif /* COMPONENTS_CPP_UTILS_RMT_H_ */ diff --git a/cpp_utils/SOC.cpp b/cpp_utils/SOC.cpp index 2f4d3a9a..fa12d4e6 100644 --- a/cpp_utils/SOC.cpp +++ b/cpp_utils/SOC.cpp @@ -34,9 +34,9 @@ void SOC::I2S::dump() { I2S0.clkm_conf.clka_en); uint32_t clockSpeed; if (I2S0.clkm_conf.clkm_div_a == 0) { - clockSpeed = 160000000/I2S0.clkm_conf.clkm_div_num; + clockSpeed = 160000000 / I2S0.clkm_conf.clkm_div_num; } else { - clockSpeed = 160000000/(I2S0.clkm_conf.clkm_div_num + I2S0.clkm_conf.clkm_div_b/I2S0.clkm_conf.clkm_div_a); + clockSpeed = 160000000 / (I2S0.clkm_conf.clkm_div_num + I2S0.clkm_conf.clkm_div_b / I2S0.clkm_conf.clkm_div_a); } printf("Clock speed: %d\n", clockSpeed); printf("\n"); @@ -44,8 +44,8 @@ void SOC::I2S::dump() { printf("I2S_CONF_REG\n"); printf("------------\n"); printf("tx_slave_mod: %s, rx_slave_mod: %s, rx_msb_right: %d, rx_right_first: %d\n", - I2S0.conf.tx_slave_mod==0?"Master":"Slave", - I2S0.conf.rx_slave_mod==0?"Master":"Slave", + (I2S0.conf.tx_slave_mod == 0) ? "Master" : "Slave", + (I2S0.conf.rx_slave_mod == 0) ? "Master" : "Slave", I2S0.conf.rx_msb_right, I2S0.conf.rx_right_first); printf("\n"); diff --git a/cpp_utils/SPI.cpp b/cpp_utils/SPI.cpp index 5ce45d15..1312e706 100644 --- a/cpp_utils/SPI.cpp +++ b/cpp_utils/SPI.cpp @@ -101,6 +101,7 @@ void SPI::setHost(spi_host_device_t host) { m_host = host; } // setHost + /** * @brief Send and receive data through %SPI. This is a blocking call. * @@ -111,7 +112,7 @@ void SPI::transfer(uint8_t* data, size_t dataLen) { assert(data != nullptr); assert(dataLen > 0); #ifdef DEBUG - for (auto i=0; i %2d %.2x", i, data[i]); } #endif @@ -141,5 +142,3 @@ uint8_t SPI::transferByte(uint8_t value) { transfer(&value, 1); return value; } // transferByte - - diff --git a/cpp_utils/SPI.h b/cpp_utils/SPI.h index d2de5d99..6ecf64e1 100644 --- a/cpp_utils/SPI.h +++ b/cpp_utils/SPI.h @@ -22,32 +22,33 @@ class SPI { int clkPin = DEFAULT_CLK_PIN, int csPin = DEFAULT_CS_PIN); void setHost(spi_host_device_t host); - void transfer(uint8_t *data, size_t dataLen); + void transfer(uint8_t* data, size_t dataLen); uint8_t transferByte(uint8_t value); + /** * @brief The default MOSI pin. */ - static const int DEFAULT_MOSI_PIN = GPIO_NUM_13; - - /** - * @brief The default MISO pin. - */ - static const int DEFAULT_MISO_PIN = GPIO_NUM_12; - - /** - * @brief The default CLK pin. - */ - static const int DEFAULT_CLK_PIN = GPIO_NUM_14; - - /** - * @brief The default CS pin. - */ - static const int DEFAULT_CS_PIN = GPIO_NUM_15; - - /** - * @brief Value of unset pin. - */ - static const int PIN_NOT_SET = -1; + static const int DEFAULT_MOSI_PIN = GPIO_NUM_13; + + /** + * @brief The default MISO pin. + */ + static const int DEFAULT_MISO_PIN = GPIO_NUM_12; + + /** + * @brief The default CLK pin. + */ + static const int DEFAULT_CLK_PIN = GPIO_NUM_14; + + /** + * @brief The default CS pin. + */ + static const int DEFAULT_CS_PIN = GPIO_NUM_15; + + /** + * @brief Value of unset pin. + */ + static const int PIN_NOT_SET = -1; private: spi_device_handle_t m_handle; diff --git a/cpp_utils/SSLUtils.cpp b/cpp_utils/SSLUtils.cpp index 92ebcfc4..36621b3e 100644 --- a/cpp_utils/SSLUtils.cpp +++ b/cpp_utils/SSLUtils.cpp @@ -20,7 +20,7 @@ SSLUtils::~SSLUtils() { void SSLUtils::setCertificate(std::string certificate) { size_t len = certificate.length(); - m_certificate = (char*)malloc(len + 1); + m_certificate = (char*) malloc(len + 1); memcpy(m_certificate, certificate.data(), len); m_certificate[len] = '\0'; } @@ -31,7 +31,7 @@ char* SSLUtils::getCertificate() { void SSLUtils::setKey(std::string key) { size_t len = key.length(); - m_key = (char*)malloc(len + 1); + m_key = (char*) malloc(len + 1); memcpy(m_key, key.data(), len); m_key[len] = '\0'; } diff --git a/cpp_utils/SmartLED.cpp b/cpp_utils/SmartLED.cpp index e0267585..430c6317 100644 --- a/cpp_utils/SmartLED.cpp +++ b/cpp_utils/SmartLED.cpp @@ -9,13 +9,13 @@ #include "string.h" #include -const char* LOG_TAG = "SmartLED"; +static const char* LOG_TAG = "SmartLED"; SmartLED::SmartLED() { m_brightness = 100; m_pixelCount = 0; m_pixels = nullptr; - m_colorOrder = (char *)"GRB"; + m_colorOrder = (char*) "GRB"; } // SmartLED @@ -32,7 +32,7 @@ SmartLED::~SmartLED() { * The LEDs are not actually updated until a call to show() is subsequently made. */ void SmartLED::clear() { - for (auto i=0; im_pixelCount; i++) { + for (auto i = 0; i < this->m_pixelCount; i++) { m_pixels[i].red = 0; m_pixels[i].green = 0; m_pixels[i].blue = 0; @@ -48,6 +48,7 @@ uint32_t SmartLED::getBrightness() { return m_brightness; } // getBrightness + /** * @brief Return the number of pixels in the chain. * @return The number of pixels in the chain as previously set by setPixelCount(). @@ -65,6 +66,7 @@ void SmartLED::setBrightness(uint32_t percent) { m_brightness = percent; } // setBrightness + /** * @brief Set the color order of data sent to the LEDs. * @@ -77,7 +79,7 @@ void SmartLED::setBrightness(uint32_t percent) { * an alternate order by supply an alternate three character string made up of 'R', 'G' and 'B' * for example "RGB". */ -void SmartLED::setColorOrder(char *colorOrder) { +void SmartLED::setColorOrder(char* colorOrder) { if (colorOrder != nullptr && strlen(colorOrder) == 3) { m_colorOrder = colorOrder; } @@ -94,9 +96,8 @@ void SmartLED::setColorOrder(char *colorOrder) { * @param [in] green The amount of green in the pixel. * @param [in] blue The amount of blue in the pixel. */ -void SmartLED::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { +void SmartLED::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { //assert(index < m_pixelCount); - m_pixels[index].red = red; m_pixels[index].green = green; m_pixels[index].blue = blue; @@ -127,12 +128,12 @@ void SmartLED::setPixel(uint16_t index, pixel_t pixel) { */ void SmartLED::setPixel(uint16_t index, uint32_t pixel) { //assert(index < m_pixelCount); - m_pixels[index].red = pixel & 0xff; m_pixels[index].green = (pixel & 0xff00) >> 8; m_pixels[index].blue = (pixel & 0xff0000) >> 16; } // setPixel + void SmartLED::setPixelCount(uint16_t pixelCount) { ESP_LOGD(LOG_TAG, ">> setPixelCount: %d", pixelCount); if (m_pixels != nullptr) { @@ -143,6 +144,7 @@ void SmartLED::setPixelCount(uint16_t pixelCount) { ESP_LOGD(LOG_TAG, "<< setPixelCount"); } + /** * @brief Set the given pixel to the specified HSB color. * @@ -153,66 +155,60 @@ void SmartLED::setPixelCount(uint16_t pixelCount) { * @param [in] saturation The amount of saturation in the pixel (0-255). * @param [in] brightness The amount of brightness in the pixel (0-255). */ -void SmartLED::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { - double sat_red; - double sat_green; - double sat_blue; - double ctmp_red; - double ctmp_green; - double ctmp_blue; - double new_red; - double new_green; - double new_blue; - double dSaturation=(double)saturation/255; - double dBrightness=(double)brightness/255; - - //assert(index < pixelCount); - - if (hue < 120) { - sat_red = (120 - hue) / 60.0; - sat_green = hue / 60.0; - sat_blue = 0; - } else if (hue < 240) { - sat_red = 0; - sat_green = (240 - hue) / 60.0; - sat_blue = (hue - 120) / 60.0; - } else { - sat_red = (hue - 240) / 60.0; - sat_green = 0; - sat_blue = (360 - hue) / 60.0; - } - - if (sat_red>1.0) { - sat_red = 1.0; - } - if (sat_green>1.0) { - sat_green = 1.0; - } - if (sat_blue>1.0) { - sat_blue = 1.0; - } - - ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); - ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); - ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); - - if (dBrightness < 0.5) { - new_red = dBrightness * ctmp_red; - new_green = dBrightness * ctmp_green; - new_blue = dBrightness * ctmp_blue; - } else { - new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; - new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; - new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; - } - - m_pixels[index].red = (uint8_t)(new_red*255); - m_pixels[index].green = (uint8_t)(new_green*255); - m_pixels[index].blue = (uint8_t)(new_blue*255); -} // setHSBPixel - - - +void SmartLED::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { + double sat_red; + double sat_green; + double sat_blue; + double ctmp_red; + double ctmp_green; + double ctmp_blue; + double new_red; + double new_green; + double new_blue; + double dSaturation=(double) saturation / 255; + double dBrightness=(double) brightness / 255; + + //assert(index < pixelCount); + + if (hue < 120) { + sat_red = (120 - hue) / 60.0; + sat_green = hue / 60.0; + sat_blue = 0; + } else if (hue < 240) { + sat_red = 0; + sat_green = (240 - hue) / 60.0; + sat_blue = (hue - 120) / 60.0; + } else { + sat_red = (hue - 240) / 60.0; + sat_green = 0; + sat_blue = (360 - hue) / 60.0; + } + if (sat_red>1.0) { + sat_red = 1.0; + } + if (sat_green>1.0) { + sat_green = 1.0; + } + if (sat_blue>1.0) { + sat_blue = 1.0; + } + ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); + ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); + ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); + + if (dBrightness < 0.5) { + new_red = dBrightness * ctmp_red; + new_green = dBrightness * ctmp_green; + new_blue = dBrightness * ctmp_blue; + } else { + new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; + new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; + new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; + } + m_pixels[index].red = (uint8_t)(new_red * 255); + m_pixels[index].green = (uint8_t)(new_green * 255); + m_pixels[index].blue = (uint8_t)(new_blue * 255); +} // setHSBPixel diff --git a/cpp_utils/SmartLED.h b/cpp_utils/SmartLED.h index e70c4843..7903f697 100644 --- a/cpp_utils/SmartLED.h +++ b/cpp_utils/SmartLED.h @@ -36,13 +36,14 @@ class SmartLED { virtual void init() = 0; virtual void show() = 0; void setBrightness(uint32_t percent); - void setColorOrder(char *order); + void setColorOrder(char* order); void setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue); void setPixel(uint16_t index, pixel_t pixel); void setPixel(uint16_t index, uint32_t pixel); void setPixelCount(uint16_t pixelCount); void setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness); void clear(); + protected: uint32_t m_brightness; char* m_colorOrder; diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 296b3a4b..32b494ab 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -61,14 +61,12 @@ SockServ::~SockServ() { * socket is placed on a queue and a semaphore signaled that a new client is available. */ /* static */ void SockServ::acceptTask(void* data) { - SockServ* pSockServ = (SockServ*)data; + SockServ* pSockServ = (SockServ*) data; try { - while(1) { + while (true) { ESP_LOGD(LOG_TAG, "Waiting on accept"); Socket tempSock = pSockServ->m_serverSocket.accept(); - if (!tempSock.isValid()) { - continue; - } + if (!tempSock.isValid()) continue; pSockServ->m_clientSet.insert(tempSock); xQueueSendToBack(pSockServ->m_acceptQueue, &tempSock, portMAX_DELAY); @@ -82,7 +80,6 @@ SockServ::~SockServ() { } // acceptTask - /** * @brief Determine the number of connected partners. * @@ -117,12 +114,12 @@ bool SockServ::getSSL() { * @return The amount of data returned or 0 if there was an error. */ size_t SockServ::receiveData(Socket s, void* pData, size_t maxData) { - int rc = s.receive((uint8_t*)pData, maxData); + int rc = s.receive((uint8_t*) pData, maxData); if (rc == -1) { ESP_LOGE(LOG_TAG, "recv(): %s", strerror(errno)); return 0; } - return rc; + return (size_t) rc; } // receiveData @@ -132,7 +129,7 @@ size_t SockServ::receiveData(Socket s, void* pData, size_t maxData) { * @param[in] str A string from which sequence of bytes will be used to send to the partner. */ void SockServ::sendData(std::string str) { - sendData((uint8_t *)str.data(), str.size()); + sendData((uint8_t*) str.data(), str.size()); } // sendData @@ -144,7 +141,7 @@ void SockServ::sendData(std::string str) { */ void SockServ::sendData(uint8_t* data, size_t length) { for (auto it = m_clientSet.begin(); it != m_clientSet.end(); ++it) { - (*it).send(data, length); + (*it).send(data, length); } } // sendData @@ -173,7 +170,7 @@ void SockServ::start() { //m_serverSocket.setSSL(m_useSSL); m_serverSocket.listen(m_port); // Create a socket and start listening on it. ESP_LOGD(LOG_TAG, "Now listening on port %d", m_port); - FreeRTOS::startTask(acceptTask, "acceptTask", this, 8*1024); + FreeRTOS::startTask(acceptTask, "acceptTask", this, 8 * 1024); } // start @@ -201,11 +198,11 @@ Socket SockServ::waitForData(std::set& socketSet) { } // End for int rc = ::select( - maxFd+1, // Number of sockets to scan &readSet, // Set of read sockets nullptr, // Set of write sockets nullptr, // Set of exception sockets nullptr // Timeout + maxFd + 1, // Number of sockets to scan ); if (rc == -1) { ESP_LOGE(LOG_TAG, "Error with select"); @@ -213,7 +210,7 @@ Socket SockServ::waitForData(std::set& socketSet) { return s; } - for ( auto it = socketSet.begin(); it != socketSet.end(); ++it) { + for (auto it = socketSet.begin(); it != socketSet.end(); ++it) { if (FD_ISSET(it->getFD(), &readSet)) { return *it; } @@ -241,5 +238,3 @@ Socket SockServ::waitForNewClient() { ESP_LOGD(LOG_TAG, "<< waitForNewClient"); return tempSocket; } // waitForNewClient - - diff --git a/cpp_utils/SockServ.h b/cpp_utils/SockServ.h index e0ffee76..770ec954 100644 --- a/cpp_utils/SockServ.h +++ b/cpp_utils/SockServ.h @@ -51,11 +51,12 @@ class SockServ { void sendData(uint8_t* data, size_t length); void sendData(std::string str); void setPort(uint16_t port); - void setSSL(bool use=true); + void setSSL(bool use = true); void start(); void stop(); Socket waitForData(std::set& socketSet); Socket waitForNewClient(); + }; #endif /* MAIN_SOCKSERV_H_ */ diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index bc5a064c..db5991ab 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -13,8 +13,6 @@ #include - - #include #include #include @@ -32,11 +30,11 @@ static const char* LOG_TAG = "Socket"; #undef bind static void my_debug( - void *ctx, + void* ctx, int level, - const char *file, + const char* file, int line, - const char *str) { + const char* str) { ((void) level); ((void) ctx); @@ -62,7 +60,7 @@ Socket Socket::accept() { ESP_LOGD(LOG_TAG, ">> accept: Accepting on %s; sockFd: %d, using SSL: %d", addressToString(&addr).c_str(), m_sock, getSSL()); struct sockaddr_in client_addr; socklen_t sin_size; - int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr *)&client_addr, &sin_size); + int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr*) &client_addr, &sin_size); //printf("------> new connection client %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (clientSockFD == -1) { SocketException se(errno); @@ -92,7 +90,7 @@ Socket Socket::accept() { * @return A string representation of the address. */ std::string Socket::addressToString(struct sockaddr* addr) { - struct sockaddr_in *pInAddr = (struct sockaddr_in *)addr; + struct sockaddr_in* pInAddr = (struct sockaddr_in*) addr; char temp[30]; char ip[20]; inet_ntop(AF_INET, &pInAddr->sin_addr, ip, sizeof(ip)); @@ -119,7 +117,7 @@ int Socket::bind(uint16_t port, uint32_t address) { serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(address); serverAddress.sin_port = htons(port); - int rc = ::lwip_bind_r(m_sock, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + int rc = ::lwip_bind_r(m_sock, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); if (rc != 0) { ESP_LOGE(LOG_TAG, "<< bind: bind[socket=%d]: %d: %s", m_sock, errno, strerror(errno)); return rc; @@ -172,7 +170,7 @@ int Socket::connect(struct in_addr address, uint16_t port) { inet_ntop(AF_INET, &address, msg, sizeof(msg)); ESP_LOGD(LOG_TAG, "Connecting to %s:[%d]", msg, port); createSocket(); - int rc = ::lwip_connect_r(m_sock, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + int rc = ::lwip_connect_r(m_sock, (struct sockaddr*) &serverAddress, sizeof(struct sockaddr_in)); if (rc == -1) { ESP_LOGE(LOG_TAG, "connect_cpp: Error: %s", strerror(errno)); close(); @@ -193,7 +191,7 @@ int Socket::connect(struct in_addr address, uint16_t port) { */ int Socket::connect(char* strAddress, uint16_t port) { struct in_addr address; - inet_pton(AF_INET, (char *)strAddress, &address); + inet_pton(AF_INET, strAddress, &address); return connect(address, port); } @@ -207,8 +205,7 @@ int Socket::createSocket(bool isDatagram) { ESP_LOGD(LOG_TAG, ">> createSocket: isDatagram: %d", isDatagram); if (isDatagram) { m_sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - } - else { + } else { m_sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } if (m_sock == -1) { @@ -290,13 +287,12 @@ bool Socket::operator <(const Socket& other) const { /** * @brief Set the socket option. */ -int Socket::setSocketOption(int option, void* value, size_t len) -{ - int res = ::setsockopt(m_sock, SOL_SOCKET, option, value, len); - if(res < 0) { - ESP_LOGE(LOG_TAG, "%X : %d", option, errno); - } - return res; +int Socket::setSocketOption(int option, void* value, size_t len) { + int res = ::setsockopt(m_sock, SOL_SOCKET, option, value, len); + if (res < 0) { + ESP_LOGE(LOG_TAG, "%X : %d", option, errno); + } + return res; } // setSocketOption @@ -304,15 +300,14 @@ int Socket::setSocketOption(int option, void* value, size_t len) * @brief Socket timeout. * @param [in] seconds to wait. */ -int Socket::setTimeout(uint32_t seconds) -{ - struct timeval tv; - tv.tv_sec = seconds; - tv.tv_usec = 0; - if(setSocketOption(SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) { - return -1; - } - return setSocketOption(SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); +int Socket::setTimeout(uint32_t seconds) { + struct timeval tv; + tv.tv_sec = seconds; + tv.tv_usec = 0; + if (setSocketOption(SO_RCVTIMEO, (char*) &tv, sizeof(struct timeval)) < 0) { + return -1; + } + return setSocketOption(SO_SNDTIMEO, (char*) &tv, sizeof(struct timeval)); } @@ -320,21 +315,15 @@ std::string Socket::readToDelim(std::string delim) { std::string ret; std::string part; auto it = delim.begin(); - while(1) { + while (true) { uint8_t val; int rc = receive(&val, 1); - if (rc == -1) { - return ""; - } - if (rc == 0) { - return ret+part; - } + if (rc == -1) return ""; + if (rc == 0) return ret + part; if (*it == val) { part+= val; ++it; - if (it == delim.end()) { - return ret; - } + if (it == delim.end()) return ret; } else { if (part.empty()) { ret += part; @@ -347,7 +336,6 @@ std::string Socket::readToDelim(std::string delim) { } // readToDelim - /** * @brief Receive data from the partner. * Receive data from the socket partner. If exact = false, we read as much data as @@ -360,13 +348,13 @@ std::string Socket::readToDelim(std::string delim) { */ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { //ESP_LOGD(LOG_TAG, ">> receive: sockFd: %d, length: %d, exact: %d", m_sock, length, exact); - if (exact == false) { + if (!exact) { int rc; if (getSSL()) { do { rc = mbedtls_ssl_read(&m_sslContext, data, length); ESP_LOGD(LOG_TAG, "rc=%d, MBEDTLS_ERR_SSL_WANT_READ=%d", rc, MBEDTLS_ERR_SSL_WANT_READ); - } while(rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); + } while (rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); } else { rc = ::lwip_recv_r(m_sock, data, length, 0); if (rc == -1) { @@ -375,16 +363,16 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { } //GeneralUtils::hexDump(data, rc); //ESP_LOGD(LOG_TAG, "<< receive: rc: %d", rc); - return rc; + return (size_t) rc; } // Read what we can, doesn't need to be an exact amount. size_t amountToRead = length; int rc; - while(amountToRead > 0) { + while (amountToRead > 0) { if (getSSL()) { do { rc = mbedtls_ssl_read(&m_sslContext, data, amountToRead); - } while(rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); + } while (rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); } else { rc = ::lwip_recv_r(m_sock, data, amountToRead, 0); } @@ -392,9 +380,7 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { ESP_LOGE(LOG_TAG, "receive: %s", strerror(errno)); return 0; } - if (rc == 0) { - break; - } + if (rc == 0) break; amountToRead -= rc; data += rc; } @@ -411,7 +397,7 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { * @param [in] pAddr An area into which we can store the address of the partner. * @return The length of the data received. */ -int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { +int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { socklen_t addrLen = sizeof(struct sockaddr); int rc = ::recvfrom(m_sock, data, length, 0, pAddr, &addrLen); return rc; @@ -430,35 +416,34 @@ int Socket::send(const uint8_t* data, size_t length) const { ESP_LOGD(LOG_TAG, "send: Raw binary of length: %d", length); //GeneralUtils::hexDump(data, length); int rc = ERR_OK; - while (length > 0) - { - if (getSSL()) { - rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); - // retry with same parameters if MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ - if ((rc != MBEDTLS_ERR_SSL_WANT_WRITE) && (rc != MBEDTLS_ERR_SSL_WANT_READ)) { - if (rc < 0) { - // no cure for other errors - log and exit - ESP_LOGE(LOG_TAG, "send: SSL write error %d", rc); - return rc; - } else { - // not all data was written, try again for the remainder - length -= rc; - data += rc; - } - } - } else { - rc = ::lwip_send_r(m_sock, data, length, 0); - if ((rc < 0) && (errno != EAGAIN)) { - // no cure for errors other than EAGAIN - log and exit - ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); - return rc; - } else if (rc > 0) { - // not all data was written, try again for the remainder - length -= rc; - data += rc; - } - } - } + while (length > 0) { + if (getSSL()) { + rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); + // retry with same parameters if MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ + if ((rc != MBEDTLS_ERR_SSL_WANT_WRITE) && (rc != MBEDTLS_ERR_SSL_WANT_READ)) { + if (rc < 0) { + // no cure for other errors - log and exit + ESP_LOGE(LOG_TAG, "send: SSL write error %d", rc); + return rc; + } else { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } + } else { + rc = ::lwip_send_r(m_sock, data, length, 0); + if ((rc < 0) && (errno != EAGAIN)) { + // no cure for errors other than EAGAIN - log and exit + ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); + return rc; + } else if (rc > 0) { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } + } return rc; } // send @@ -471,19 +456,19 @@ int Socket::send(const uint8_t* data, size_t length) const { */ int Socket::send(std::string value) const { ESP_LOGD(LOG_TAG, "send: Binary of length: %d", value.length()); - return send((uint8_t *)value.data(), value.size()); + return send((uint8_t*) value.data(), value.size()); } // send int Socket::send(uint16_t value) { ESP_LOGD(LOG_TAG, "send: 16bit value: %.2x", value); - return send((uint8_t *)&value, sizeof(value)); + return send((uint8_t*) &value, sizeof(value)); } // send_cpp int Socket::send(uint32_t value) { ESP_LOGD(LOG_TAG, "send: 32bit value: %.2x", value); - return send((uint8_t *)&value, sizeof(value)); + return send((uint8_t*) &value, sizeof(value)); } // send @@ -512,7 +497,7 @@ void Socket::sendTo(const uint8_t* data, size_t length, struct sockaddr* pAddr) */ void Socket::setReuseAddress(bool value) { ESP_LOGD(LOG_TAG, ">> setReuseAddress: %d", value); - int val = value?1:0; + int val = value ? 1 : 0; setSocketOption(SO_REUSEADDR, &val, sizeof(val)); ESP_LOGD(LOG_TAG, "<< setReuseAddress"); } // setReuseAddress @@ -527,9 +512,9 @@ void Socket::setSSL(bool sslValue) { ESP_LOGD(LOG_TAG, ">> setSSL: %s", sslValue?"Yes":"No"); m_useSSL = sslValue; - if (sslValue == true) { - char* pvtKey = SSLUtils::getKey(); - char* certificate = SSLUtils::getCertificate(); + if (sslValue) { + char *pvtKey = SSLUtils::getKey(); + char *certificate = SSLUtils::getCertificate(); if (pvtKey == nullptr) { ESP_LOGE(LOG_TAG, "No private key file"); return; @@ -547,66 +532,56 @@ void Socket::setSSL(bool sslValue) { mbedtls_entropy_init(&m_entropy); mbedtls_ctr_drbg_init(&m_ctr_drbg); - int ret = mbedtls_x509_crt_parse(&m_srvcert, (unsigned char*)certificate, strlen(certificate)+1); - if( ret != 0 ) { - ESP_LOGD(LOG_TAG, "mbedtls_x509_crt_parse returned 0x%x", -ret ); + int ret = mbedtls_x509_crt_parse(&m_srvcert, (unsigned char *) certificate, strlen(certificate) + 1); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_x509_crt_parse returned 0x%x", -ret); goto exit; } - ret = mbedtls_pk_parse_key(&m_pkey, (unsigned char*)pvtKey, strlen(pvtKey)+1, NULL, 0 ); - if( ret != 0 ) { + ret = mbedtls_pk_parse_key(&m_pkey, (unsigned char *) pvtKey, strlen(pvtKey) + 1, NULL, 0); + if (ret != 0) { ESP_LOGD(LOG_TAG, "mbedtls_pk_parse_key returned 0x%x", -ret); goto exit; } - ret = mbedtls_ctr_drbg_seed( &m_ctr_drbg, mbedtls_entropy_func, &m_entropy, (const unsigned char*) pers, strlen(pers)); - if( ret != 0 ) { - ESP_LOGD(LOG_TAG, "! mbedtls_ctr_drbg_seed returned %d\n",ret); - goto exit; - } + ret = mbedtls_ctr_drbg_seed(&m_ctr_drbg, mbedtls_entropy_func, &m_entropy, (const unsigned char*) pers, strlen(pers)); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } ret = mbedtls_ssl_config_defaults(&m_conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if(ret != 0 ) { + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { ESP_LOGD(LOG_TAG, "mbedtls_ssl_config_defaults returned %d\n\n", ret); goto exit; } mbedtls_ssl_conf_authmode(&m_conf, MBEDTLS_SSL_VERIFY_NONE); - mbedtls_ssl_conf_rng(&m_conf, mbedtls_ctr_drbg_random, &m_ctr_drbg); +// mbedtls_ssl_conf_ca_chain( &m_conf, m_srvcert.next, NULL); + ret = mbedtls_ssl_conf_own_cert(&m_conf, &m_srvcert, &m_pkey); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + goto exit; + } -// mbedtls_ssl_conf_ca_chain( &m_conf, m_srvcert.next, NULL); - ret = mbedtls_ssl_conf_own_cert( &m_conf, &m_srvcert, &m_pkey); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_conf_own_cert returned %d\n\n", ret); - goto exit; - } - - mbedtls_ssl_conf_dbg(&m_conf, my_debug, nullptr); + mbedtls_ssl_conf_dbg(&m_conf, my_debug, nullptr); #ifdef CONFIG_MBEDTLS_DEBUG - mbedtls_debug_set_threshold(4); + mbedtls_debug_set_threshold(4); #endif - ret = mbedtls_ssl_setup(&m_sslContext, &m_conf); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_setup returned %d\n\n", ret ); - goto exit; - } -/* - ret = mbedtls_ssl_set_hostname(&m_sslContext, "192.168.1.99"); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_set_hostname returned %d\n\n", ret ); - goto exit; - } - */ - - exit: - return; + ret = mbedtls_ssl_setup(&m_sslContext, &m_conf); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } } +exit: + return; } // setSSL @@ -619,14 +594,12 @@ void Socket::sslHandshake() { ESP_LOGD(LOG_TAG, " - Reset complete"); mbedtls_ssl_set_bio(&m_sslContext, &m_sslSock, mbedtls_net_send, mbedtls_net_recv, NULL); - while(1) { + while (true) { int ret = mbedtls_ssl_handshake(&m_sslContext); - if (ret == 0) { - break; - } + if (ret == 0) break; - if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_handshake returned %d\n\n", ret ); + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_handshake returned %d\n\n", ret); return; } } // End while @@ -675,13 +648,10 @@ SocketInputRecordStreambuf::~SocketInputRecordStreambuf() { * */ SocketInputRecordStreambuf::int_type SocketInputRecordStreambuf::underflow() { - if (m_sizeRead >= m_dataLength) { - return EOF; - } - int bytesRead = m_socket.receive((uint8_t*)m_buffer, m_bufferSize, true); - if (bytesRead == 0) { - return EOF; - } + if (m_sizeRead >= m_dataLength) return EOF; + int bytesRead = m_socket.receive((uint8_t*) m_buffer, m_bufferSize, true); + if (bytesRead == 0) return EOF; + m_sizeRead += bytesRead; setg(m_buffer, m_buffer, m_buffer + bytesRead); return traits_type::to_int_type(*gptr()); @@ -690,5 +660,3 @@ SocketInputRecordStreambuf::int_type SocketInputRecordStreambuf::underflow() { SocketException::SocketException(int myErrno) { m_errno = myErrno; } - - diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 6804ab93..f9a2bac6 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -45,10 +45,13 @@ class SocketException: public std::exception { public: SocketException(int myErrno); + private: int m_errno; + }; + /** * @brief Encapsulate a socket. * @@ -75,43 +78,46 @@ class Socket { int getFD() const; bool getSSL() const; bool isValid(); - int listen(uint16_t port, bool isDatagram=false, bool reuseAddress=false); + int listen(uint16_t port, bool isDatagram = false, bool reuseAddress = false); bool operator<(const Socket& other) const; std::string readToDelim(std::string delim); - size_t receive(uint8_t* data, size_t length, bool exact=false); + size_t receive(uint8_t* data, size_t length, bool exact = false); int receiveFrom(uint8_t* data, size_t length, struct sockaddr* pAddr); int send(std::string value) const; int send(const uint8_t* data, size_t length) const; int send(uint16_t value); int send(uint32_t value); void sendTo(const uint8_t* data, size_t length, struct sockaddr* pAddr); - void setSSL(bool sslValue=true); + void setSSL(bool sslValue = true); std::string toString(); private: int m_sock; // The underlying TCP/IP socket bool m_useSSL; // Should we use SSL - mbedtls_net_context m_sslSock; - mbedtls_entropy_context m_entropy; - mbedtls_ctr_drbg_context m_ctr_drbg; - mbedtls_ssl_context m_sslContext; - mbedtls_ssl_config m_conf; - mbedtls_x509_crt m_srvcert; - mbedtls_pk_context m_pkey; - void sslHandshake(); + mbedtls_net_context m_sslSock; + mbedtls_entropy_context m_entropy; + mbedtls_ctr_drbg_context m_ctr_drbg; + mbedtls_ssl_context m_sslContext; + mbedtls_ssl_config m_conf; + mbedtls_x509_crt m_srvcert; + mbedtls_pk_context m_pkey; + void sslHandshake(); + }; class SocketInputRecordStreambuf : public std::streambuf { public: - SocketInputRecordStreambuf(Socket socket, size_t dataLength, size_t bufferSize=512); + SocketInputRecordStreambuf(Socket socket, size_t dataLength, size_t bufferSize = 512); ~SocketInputRecordStreambuf(); int_type underflow(); + private: char* m_buffer; Socket m_socket; size_t m_dataLength; size_t m_bufferSize; size_t m_sizeRead; + }; diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index e9e7c525..3b774db4 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -47,9 +47,6 @@ typedef volatile struct { } pin_ctrl; // The 36 exposed pads. - io_mux_reg_t pad_gpio36; // GPIO36 - io_mux_reg_t pad_gpio37; // GPIO37 - io_mux_reg_t pad_gpio38; // GPIO38 io_mux_reg_t pad_gpio39; // GPIO39 io_mux_reg_t pad_gpio34; // GPIO34 io_mux_reg_t pad_gpio35; // GPIO35 @@ -67,6 +64,7 @@ typedef volatile struct { io_mux_reg_t pad_gpio4; // GPIO4 io_mux_reg_t pad_gpio16; // GPIO16 io_mux_reg_t pad_gpio17; // GPIO17 + io_mux_reg_t pad_gpio37; // GPIO37 io_mux_reg_t pad_sd_data2; // GPIO9 io_mux_reg_t pad_sd_data3; // GPIO10 io_mux_reg_t pad_sd_cmd; // GPIO11 @@ -85,7 +83,7 @@ typedef volatile struct { io_mux_reg_t pad_gpio24; // GPIO24 } io_mux_dev_t; -static io_mux_dev_t* IO_MUX = (io_mux_dev_t*)0x3ff49000; +static io_mux_dev_t* IO_MUX = (io_mux_dev_t*) 0x3ff49000; static const io_mux_reg_t* io_mux_translate[] = { &IO_MUX->pad_gpio0, // 0 @@ -110,12 +108,6 @@ static const io_mux_reg_t* io_mux_translate[] = { &IO_MUX->pad_gpio19, // 19 &IO_MUX->pad_gpio20, // 20 &IO_MUX->pad_gpio21, // 21 - &IO_MUX->pad_gpio22, // 22 - &IO_MUX->pad_gpio23, // 23 - &IO_MUX->pad_gpio24, // 24 - &IO_MUX->pad_gpio25, // 25 - &IO_MUX->pad_gpio26, // 26 - &IO_MUX->pad_gpio27, // 27 nullptr, // 28 nullptr, // 29 nullptr, // 30 @@ -411,7 +403,7 @@ const static char* outSignalStrings[] = { printf("GPIO_FUNCn_OUT_SEL_CFG_REG\n"); printf("--------------------------\n"); printf("%3s %4s\n", "Pin", "Func"); - for (int i=0; iblockNumber); - dp.data = std::string((char *)&pRecv_data->data, receivedSize-4); + dp.data = std::string((char*) &pRecv_data->data, receivedSize - 4); fwrite(dp.data.data(), dp.data.length(), 1, file); sendAck(dp.blockNumber); - ESP_LOGD(tag, "Block size: %d", dp.data.length()); + ESP_LOGD(LOG_TAG, "Block size: %d", dp.data.length()); if (dp.data.length() < TFTP_DATA_SIZE) { finished = true; } @@ -213,8 +212,8 @@ void TFTP::TFTP_Transaction::sendAck(uint16_t blockNumber) { ackData.opCode = htons(TFTP_OPCODE_ACK); ackData.blockNumber = htons(blockNumber); - ESP_LOGD(tag, "Sending ack to %s, blockNumber=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber); - m_partnerSocket.sendTo((uint8_t *)&ackData, sizeof(ackData), &m_partnerAddress); + ESP_LOGD(LOG_TAG, "Sending ack to %s, blockNumber=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber); + m_partnerSocket.sendTo((uint8_t*) &ackData, sizeof(ackData), &m_partnerAddress); } // sendAck @@ -233,26 +232,29 @@ void TFTP::start(uint16_t port) { * or write a file to the server. Once we have received a request we then call the appropriate * handler to handle that type of request. When the request has been completed, we start again. */ - ESP_LOGD(tag, "Starting TFTP::start() on port %d", port); + ESP_LOGD(LOG_TAG, "Starting TFTP::start() on port %d", port); Socket serverSocket; serverSocket.listen(port, true); // Create a listening socket that is a datagram. - while(true) { + while (true) { // This would be a good place to start a transaction in the background. - TFTP_Transaction *pTFTPTransaction = new TFTP_Transaction(); + TFTP_Transaction* pTFTPTransaction = new TFTP_Transaction(); pTFTPTransaction->setBaseDir(m_baseDir); uint16_t receivedOpCode = pTFTPTransaction->waitForRequest(&serverSocket); - switch(receivedOpCode) { - // Handle the write request (client file upload) + switch (receivedOpCode) { + // Handle the write request (client file upload) case opcode::TFTP_OPCODE_WRQ: { pTFTPTransaction->processWRQ(); break; } - // Handle the read request (server file download) + // Handle the read request (server file download) case opcode::TFTP_OPCODE_RRQ: { pTFTPTransaction->processRRQ(); break; } + default: + ESP_LOGE(LOG_TAG, "Unknown opcode: %d", receivedOpCode); + break; } delete pTFTPTransaction; } // End while loop @@ -292,12 +294,12 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { uint16_t blockNumber; } ackData; - ESP_LOGD(tag, "TFTP: Waiting for an acknowledgment request"); - int sizeRead = m_partnerSocket.receiveFrom((uint8_t *)&ackData, sizeof(ackData), &m_partnerAddress); - ESP_LOGD(tag, "TFTP: Received some data."); + ESP_LOGD(LOG_TAG, "TFTP: Waiting for an acknowledgment request"); + int sizeRead = m_partnerSocket.receiveFrom((uint8_t*) &ackData, sizeof(ackData), &m_partnerAddress); + ESP_LOGD(LOG_TAG, "TFTP: Received some data."); if (sizeRead != sizeof(ackData)) { - ESP_LOGE(tag, "waitForAck: Received %d but expected %d", sizeRead, sizeof(ackData)); + ESP_LOGE(LOG_TAG, "waitForAck: Received %d but expected %d", sizeRead, sizeof(ackData)); sendError(ERROR_CODE_NOTDEFINED, "Ack not correct size"); return; } @@ -306,12 +308,12 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { ackData.blockNumber = ntohs(ackData.blockNumber); if (ackData.opCode != opcode::TFTP_OPCODE_ACK) { - ESP_LOGE(tag, "waitForAck: Received opcode %d but expected %d", ackData.opCode, opcode::TFTP_OPCODE_ACK); + ESP_LOGE(LOG_TAG, "waitForAck: Received opcode %d but expected %d", ackData.opCode, opcode::TFTP_OPCODE_ACK); return; } if (ackData.blockNumber != blockNumber) { - ESP_LOGE(tag, "waitForAck: Blocknumber received %d but expected %d", ackData.blockNumber, blockNumber); + ESP_LOGE(LOG_TAG, "waitForAck: Blocknumber received %d but expected %d", ackData.blockNumber, blockNumber); return; } } // waitForAck @@ -326,29 +328,28 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { * @param pServerSocket The server socket on which to listen for client requests. * @return The op code received. */ -uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { /* * 2 bytes string 1 byte string 1 byte * ----------------------------------------------- * RRQ/ | 01/02 | Filename | 0 | Mode | 0 | * WRQ ----------------------------------------------- */ +uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket* pServerSocket) { union { uint8_t buf[TFTP_DATA_SIZE]; uint16_t opCode; } record; size_t length = 100; - ESP_LOGD(tag, "TFTP: Waiting for a request"); + ESP_LOGD(LOG_TAG, "TFTP: Waiting for a request"); pServerSocket->receiveFrom(record.buf, length, &m_partnerAddress); // Save the filename, mode and op code. - m_filename = std::string((char *)(record.buf + 2)); - m_mode = std::string((char *)(record.buf + 3 + m_filename.length())); + m_filename = std::string((char*) (record.buf + 2)); + m_mode = std::string((char*) (record.buf + 3 + m_filename.length())); m_opCode = ntohs(record.opCode); - switch(m_opCode) { - + switch (m_opCode) { // Handle the Write Request command. case TFTP_OPCODE_WRQ: { m_partnerSocket.createSocket(true); @@ -357,7 +358,6 @@ uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { break; } - // Handle the Read request command. case TFTP_OPCODE_RRQ: { m_partnerSocket.createSocket(true); @@ -366,7 +366,7 @@ uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { } default: { - ESP_LOGD(tag, "Un-handled opcode: %d", m_opCode); + ESP_LOGD(LOG_TAG, "Un-handled opcode: %d", m_opCode); break; } } @@ -387,10 +387,10 @@ void TFTP::TFTP_Transaction::sendError(uint16_t code, std::string message) { * ----------------------------------------- */ int size = 2 + 2 + message.length() + 1; - uint8_t *buf = (uint8_t *)malloc(size); - *(uint16_t *)(&buf[0]) = htons(opcode::TFTP_OPCODE_ERROR); - *(uint16_t *)(&buf[2]) = htons(code); - strcpy((char *)(&buf[4]), message.c_str()); + uint8_t* buf = (uint8_t*) malloc(size); + *(uint16_t*) (&buf[0]) = htons(opcode::TFTP_OPCODE_ERROR); + *(uint16_t*) (&buf[2]) = htons(code); + strcpy((char*) (&buf[4]), message.c_str()); m_partnerSocket.sendTo(buf, size, &m_partnerAddress); free(buf); } // sendError diff --git a/cpp_utils/TFTP.h b/cpp_utils/TFTP.h index 3f40bd2b..56733598 100644 --- a/cpp_utils/TFTP.h +++ b/cpp_utils/TFTP.h @@ -36,7 +36,7 @@ class TFTP { public: TFTP(); virtual ~TFTP(); - void start(uint16_t port=TFTP_DEFAULT_PORT); + void start(uint16_t port = TFTP_DEFAULT_PORT); void setBaseDir(std::string baseDir); /** * @brief Internal class for %TFTP processing. @@ -50,7 +50,8 @@ class TFTP { void sendError(uint16_t code, std::string message); void setBaseDir(std::string baseDir); void waitForAck(uint16_t blockNumber); - uint16_t waitForRequest(Socket *pServerSocket); + uint16_t waitForRequest(Socket* pServerSocket); + private: /** * Socket on which the server will communicate with the client.. @@ -61,9 +62,12 @@ class TFTP { std::string m_filename; // The name of the file. std::string m_mode; std::string m_baseDir; // The base directory. + }; + private: std::string m_baseDir; + }; #endif /* COMPONENTS_CPP_UTILS_TFTP_H_ */ diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index 2177b888..43f587e2 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -14,7 +14,7 @@ #include "Task.h" #include "sdkconfig.h" -static char tag[] = "Task"; +static const char* LOG_TAG = "Task"; /** @@ -44,7 +44,7 @@ Task::~Task() { */ /* static */ void Task::delay(int ms) { - ::vTaskDelay(ms/portTICK_PERIOD_MS); + ::vTaskDelay(ms / portTICK_PERIOD_MS); } // delay /** @@ -54,10 +54,10 @@ Task::~Task() { * @param [in] pTaskInstance The task to run. */ void Task::runTask(void* pTaskInstance) { - Task* pTask = (Task*)pTaskInstance; - ESP_LOGD(tag, ">> runTask: taskName=%s", pTask->m_taskName.c_str()); + Task* pTask = (Task*) pTaskInstance; + ESP_LOGD(LOG_TAG, ">> runTask: taskName=%s", pTask->m_taskName.c_str()); pTask->run(pTask->m_taskData); - ESP_LOGD(tag, "<< runTask: taskName=%s", pTask->m_taskName.c_str()); + ESP_LOGD(LOG_TAG, "<< runTask: taskName=%s", pTask->m_taskName.c_str()); pTask->stop(); } // runTask @@ -69,7 +69,7 @@ void Task::runTask(void* pTaskInstance) { */ void Task::start(void* taskData) { if (m_handle != nullptr) { - ESP_LOGW(tag, "Task::start - There might be a task already running!"); + ESP_LOGW(LOG_TAG, "Task::start - There might be a task already running!"); } m_taskData = taskData; ::xTaskCreatePinnedToCore(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle, m_coreId); @@ -82,9 +82,7 @@ void Task::start(void* taskData) { * @return N/A. */ void Task::stop() { - if (m_handle == nullptr) { - return; - } + if (m_handle == nullptr) return; xTaskHandle temp = m_handle; m_handle = nullptr; ::vTaskDelete(temp); diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index f7d88754..5eb4966b 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -33,13 +33,13 @@ */ class Task { public: - Task(std::string taskName="Task", uint16_t stackSize=10000, uint8_t priority=5); + Task(std::string taskName = "Task", uint16_t stackSize = 10000, uint8_t priority = 5); virtual ~Task(); void setStackSize(uint16_t stackSize); void setPriority(uint8_t priority); void setName(std::string name); void setCore(BaseType_t coreId); - void start(void* taskData=nullptr); + void start(void* taskData = nullptr); void stop(); /** * @brief Body of the task to execute. @@ -50,17 +50,18 @@ class Task { * * @param [in] data The data passed in to the newly started task. */ - virtual void run(void *data) = 0; // Make run pure virtual + virtual void run(void* data) = 0; // Make run pure virtual static void delay(int ms); private: xTaskHandle m_handle; void* m_taskData; - static void runTask(void *data); + static void runTask(void* data); std::string m_taskName; uint16_t m_stackSize; uint8_t m_priority; BaseType_t m_coreId; + }; #endif /* COMPONENTS_CPP_UTILS_TASK_H_ */ diff --git a/cpp_utils/U8G2.h b/cpp_utils/U8G2.h index db57ba64..386272fc 100644 --- a/cpp_utils/U8G2.h +++ b/cpp_utils/U8G2.h @@ -24,7 +24,7 @@ class U8G2 { u8g2_ClearBuffer(&m_u8g2); } - void drawBitmap(uint32_t x, uint32_t y, uint32_t cnt, uint32_t h, const uint8_t *bitmap) { + void drawBitmap(uint32_t x, uint32_t y, uint32_t cnt, uint32_t h, const uint8_t* bitmap) { u8g2_DrawBitmap(&m_u8g2, x, y, cnt, h, bitmap); } @@ -75,7 +75,6 @@ class U8G2 { u8g2_DrawRFrame(&m_u8g2, x, y, w, h, r); } - uint32_t drawStr(uint32_t x, uint32_t y, std::string s) { return u8g2_DrawStr(&m_u8g2, x, y, s.c_str()); } @@ -92,11 +91,11 @@ class U8G2 { u8g2_DrawVLine(&m_u8g2, x, y, h); } - int8_t getAscent(void) { + int8_t getAscent() { return u8g2_GetAscent(&m_u8g2); } - int8_t getDescent(void) { + int8_t getDescent() { return u8g2_GetDescent(&m_u8g2); } @@ -107,19 +106,23 @@ class U8G2 { void initDisplay() { u8g2_InitDisplay(&m_u8g2); } + void sendBuffer() { u8g2_SendBuffer(&m_u8g2); } - void setFont(const uint8_t *font) { + void setFont(const uint8_t* font) { u8g2_SetFont(&m_u8g2, font); } + void setPowerSave(uint8_t is_enable) { u8g2_SetPowerSave(&m_u8g2, is_enable); // wake up display } private: u8g2_t m_u8g2; + }; + #endif // CONFIG_U8G2_PRESENT #endif /* COMPONENTS_CPP_UTILS_U8G2_H_ */ diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index 624480f0..c0f622d7 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -33,7 +33,7 @@ static const char* LOG_TAG = "WS2812"; * a logic 1 for 0.7us * a logic 0 for 0.6us */ -static void setItem1(rmt_item32_t *pItem) { +static void setItem1(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 1; pItem->duration0 = 10; @@ -49,7 +49,7 @@ static void setItem1(rmt_item32_t *pItem) { * a logic 1 for 0.35us * a logic 0 for 0.8us */ -static void setItem0(rmt_item32_t *pItem) { +static void setItem0(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 1; pItem->duration0 = 4; @@ -61,7 +61,7 @@ static void setItem0(rmt_item32_t *pItem) { /** * Add an RMT terminator into the RMT data. */ -static void setTerminator(rmt_item32_t *pItem) { +static void setTerminator(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 0; pItem->duration0 = 0; @@ -74,7 +74,7 @@ static void setTerminator(rmt_item32_t *pItem) { * type which should be one of 'R', 'G' or 'B'. */ static uint8_t getChannelValueByType(char type, pixel_t pixel) { - switch(type) { + switch (type) { case 'r': case 'R': return pixel.red; @@ -84,13 +84,13 @@ static uint8_t getChannelValueByType(char type, pixel_t pixel) { case 'g': case 'G': return pixel.green; + default: + ESP_LOGW(LOG_TAG, "Unknown color channel 0x%2x", type); + return 0; } - ESP_LOGW(LOG_TAG, "Unknown color channel 0x%2x", type); - return 0; } // getChannelValueByType - /** * @brief Construct a wrapper for the pixels. * @@ -113,7 +113,7 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { assert(ESP32CPP::GPIO::inRange(dinPin)); this->pixelCount = pixelCount; - this->channel = (rmt_channel_t)channel; + this->channel = (rmt_channel_t) channel; // The number of items is number of pixels * 24 bits per pixel + the terminator. // Remember that an item is TWO RMT output bits ... for NeoPixels this is correct because @@ -121,24 +121,23 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { this->items = new rmt_item32_t[pixelCount * 24 + 1]; this->pixels = new pixel_t[pixelCount]; - this->colorOrder = (char *)"GRB"; + this->colorOrder = (char*) "GRB"; clear(); rmt_config_t config; config.rmt_mode = RMT_MODE_TX; config.channel = this->channel; config.gpio_num = dinPin; - config.mem_block_num = 8-this->channel; + config.mem_block_num = 8 - this->channel; config.clk_div = 8; config.tx_config.loop_en = 0; config.tx_config.carrier_en = 0; config.tx_config.idle_output_en = 1; - config.tx_config.idle_level = (rmt_idle_level_t)0; + config.tx_config.idle_level = (rmt_idle_level_t) 0; config.tx_config.carrier_freq_hz = 10000; config.tx_config.carrier_level = (rmt_carrier_level_t)1; config.tx_config.carrier_duty_percent = 50; - ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(this->channel, 0, 0)); } // WS2812 @@ -152,19 +151,19 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { void WS2812::show() { auto pCurrentItem = this->items; - for (auto i=0; ipixelCount; i++) { + for (uint16_t i = 0; i < this->pixelCount; i++) { uint32_t currentPixel = (getChannelValueByType(this->colorOrder[0], this->pixels[i]) << 16) | (getChannelValueByType(this->colorOrder[1], this->pixels[i]) << 8) | (getChannelValueByType(this->colorOrder[2], this->pixels[i])); ESP_LOGD(LOG_TAG, "Pixel value: %x", currentPixel); - for (int j=23; j>=0; j--) { + for (uint8_t j = 23; j > =0; j--) { // We have 24 bits of data representing the red, green amd blue channels. The value of the // 24 bits to output is in the variable current_pixel. We now need to stream this value // through RMT in most significant bit first. To do this, we iterate through each of the 24 // bits from MSB to LSB. - if (currentPixel & (1<channel, this->items, this->pixelCount*24, 1 /* wait till done */)); + ESP_ERROR_CHECK(rmt_write_items(this->channel, this->items, this->pixelCount * 24, 1 /* wait till done */)); } // show @@ -191,7 +190,7 @@ void WS2812::show() { * an alternate order by supply an alternate three character string made up of 'R', 'G' and 'B' * for example "RGB". */ -void WS2812::setColorOrder(char *colorOrder) { +void WS2812::setColorOrder(char* colorOrder) { if (colorOrder != nullptr && strlen(colorOrder) == 3) { this->colorOrder = colorOrder; } @@ -208,14 +207,14 @@ void WS2812::setColorOrder(char *colorOrder) { * @param [in] green The amount of green in the pixel. * @param [in] blue The amount of blue in the pixel. */ -void WS2812::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { +void WS2812::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { assert(index < pixelCount); - this->pixels[index].red = red; this->pixels[index].green = green; this->pixels[index].blue = blue; } // setPixel + /** * @brief Set the given pixel to the specified color. * @@ -240,7 +239,6 @@ void WS2812::setPixel(uint16_t index, pixel_t pixel) { */ void WS2812::setPixel(uint16_t index, uint32_t pixel) { assert(index < pixelCount); - this->pixels[index].red = pixel & 0xff; this->pixels[index].green = (pixel & 0xff00) >> 8; this->pixels[index].blue = (pixel & 0xff0000) >> 16; @@ -256,64 +254,65 @@ void WS2812::setPixel(uint16_t index, uint32_t pixel) { * @param [in] saturation The amount of saturation in the pixel (0-255). * @param [in] brightness The amount of brightness in the pixel (0-255). */ -void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { - double sat_red; - double sat_green; - double sat_blue; - double ctmp_red; - double ctmp_green; - double ctmp_blue; - double new_red; - double new_green; - double new_blue; - double dSaturation=(double)saturation/255; - double dBrightness=(double)brightness/255; - - assert(index < pixelCount); - - if (hue < 120) { - sat_red = (120 - hue) / 60.0; - sat_green = hue / 60.0; - sat_blue = 0; - } else if (hue < 240) { - sat_red = 0; - sat_green = (240 - hue) / 60.0; - sat_blue = (hue - 120) / 60.0; - } else { - sat_red = (hue - 240) / 60.0; - sat_green = 0; - sat_blue = (360 - hue) / 60.0; - } - - if (sat_red>1.0) { - sat_red=1.0; - } - if (sat_green>1.0) { - sat_green=1.0; - } - if (sat_blue>1.0) { - sat_blue=1.0; - } - - ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); - ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); - ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); - - if (dBrightness < 0.5) { - new_red = dBrightness * ctmp_red; - new_green = dBrightness * ctmp_green; - new_blue = dBrightness * ctmp_blue; - } else { - new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; - new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; - new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; - } - - this->pixels[index].red = (uint8_t)(new_red*255); - this->pixels[index].green = (uint8_t)(new_green*255); - this->pixels[index].blue = (uint8_t)(new_blue*255); +void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { + double sat_red; + double sat_green; + double sat_blue; + double ctmp_red; + double ctmp_green; + double ctmp_blue; + double new_red; + double new_green; + double new_blue; + double dSaturation = (double) saturation / 255; + double dBrightness = (double) brightness / 255; + + assert(index < pixelCount); + + if (hue < 120) { + sat_red = (120 - hue) / 60.0; + sat_green = hue / 60.0; + sat_blue = 0; + } else if (hue < 240) { + sat_red = 0; + sat_green = (240 - hue) / 60.0; + sat_blue = (hue - 120) / 60.0; + } else { + sat_red = (hue - 240) / 60.0; + sat_green = 0; + sat_blue = (360 - hue) / 60.0; + } + + if (sat_red > 1.0) { + sat_red = 1.0; + } + if (sat_green > 1.0) { + sat_green = 1.0; + } + if (sat_blue > 1.0) { + sat_blue = 1.0; + } + + ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); + ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); + ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); + + if (dBrightness < 0.5) { + new_red = dBrightness * ctmp_red; + new_green = dBrightness * ctmp_green; + new_blue = dBrightness * ctmp_blue; + } else { + new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; + new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; + new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; + } + + this->pixels[index].red = (uint8_t)(new_red * 255); + this->pixels[index].green = (uint8_t)(new_green * 255); + this->pixels[index].blue = (uint8_t)(new_blue * 255); } // setHSBPixel + /** * @brief Clear all the pixel colors. * @@ -321,13 +320,14 @@ void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8 * The LEDs are not actually updated until a call to show(). */ void WS2812::clear() { - for (auto i=0; ipixelCount; i++) { + for (uint16_t i = 0; i < this->pixelCount; i++) { this->pixels[i].red = 0; this->pixels[i].green = 0; this->pixels[i].blue = 0; } } // clear + /** * @brief Class instance destructor. */ diff --git a/cpp_utils/WS2812.h b/cpp_utils/WS2812.h index f599c1b7..f11cbc89 100644 --- a/cpp_utils/WS2812.h +++ b/cpp_utils/WS2812.h @@ -48,21 +48,23 @@ typedef struct { */ class WS2812 { public: - WS2812(gpio_num_t gpioNum, uint16_t pixelCount, int channel=RMT_CHANNEL_0); + WS2812(gpio_num_t gpioNum, uint16_t pixelCount, int channel = RMT_CHANNEL_0); void show(); - void setColorOrder(char *order); + void setColorOrder(char* order); void setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue); void setPixel(uint16_t index, pixel_t pixel); void setPixel(uint16_t index, uint32_t pixel); void setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness); void clear(); virtual ~WS2812(); + private: - char *colorOrder; + char* colorOrder; uint16_t pixelCount; rmt_channel_t channel; - rmt_item32_t *items; - pixel_t *pixels; + rmt_item32_t* items; + pixel_t* pixels; + }; #endif /* MAIN_WS2812_H_ */ diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index c250a4d5..0fc666af 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -18,107 +18,113 @@ #include #include -static char tag[] = "WebServer"; +#define STATE_NAME 0 +#define STATE_VALUE 1 + +static const char* LOG_TAG = "WebServer"; struct WebServerUserData { - WebServer *pWebServer; - WebServer::HTTPMultiPart *pMultiPart; - WebServer::WebSocketHandler *pWebSocketHandler; - void *originalUserData; + WebServer* pWebServer; + WebServer::HTTPMultiPart* pMultiPart; + WebServer::WebSocketHandler* pWebSocketHandler; + void* originalUserData; }; + /** * @brief Convert a Mongoose event type to a string. * @param [in] event The received event type. * @return The string representation of the event. */ static std::string mongoose_eventToString(int event) { - switch (event) { - case MG_EV_CONNECT: - return "MG_EV_CONNECT"; - case MG_EV_ACCEPT: - return "MG_EV_ACCEPT"; - case MG_EV_CLOSE: - return "MG_EV_CLOSE"; - case MG_EV_SEND: - return "MG_EV_SEND"; - case MG_EV_RECV: - return "MG_EV_RECV"; - case MG_EV_POLL: - return "MG_EV_POLL"; - case MG_EV_TIMER: - return "MG_EV_TIMER"; - case MG_EV_HTTP_PART_DATA: - return "MG_EV_HTTP_PART_DATA"; - case MG_EV_HTTP_MULTIPART_REQUEST: - return "MG_EV_HTTP_MULTIPART_REQUEST"; - case MG_EV_HTTP_PART_BEGIN: - return "MG_EV_HTTP_PART_BEGIN"; - case MG_EV_HTTP_PART_END: - return "MG_EV_HTTP_PART_END"; - case MG_EV_HTTP_MULTIPART_REQUEST_END: - return "MG_EV_HTTP_MULTIPART_REQUEST_END"; - case MG_EV_HTTP_REQUEST: - return "MG_EV_HTTP_REQUEST"; - case MG_EV_HTTP_REPLY: - return "MG_EV_HTTP_REPLY"; - case MG_EV_HTTP_CHUNK: - return "MG_EV_HTTP_CHUNK"; - case MG_EV_MQTT_CONNACK: - return "MG_EV_MQTT_CONNACK"; - case MG_EV_MQTT_CONNECT: - return "MG_EV_MQTT_CONNECT"; - case MG_EV_MQTT_DISCONNECT: - return "MG_EV_MQTT_DISCONNECT"; - case MG_EV_MQTT_PINGREQ: - return "MG_EV_MQTT_PINGREQ"; - case MG_EV_MQTT_PINGRESP: - return "MG_EV_MQTT_PINGRESP"; - case MG_EV_MQTT_PUBACK: - return "MG_EV_MQTT_PUBACK"; - case MG_EV_MQTT_PUBCOMP: - return "MG_EV_MQTT_PUBCOMP"; - case MG_EV_MQTT_PUBLISH: - return "MG_EV_MQTT_PUBLISH"; - case MG_EV_MQTT_PUBREC: - return "MG_EV_MQTT_PUBREC"; - case MG_EV_MQTT_PUBREL: - return "MG_EV_MQTT_PUBREL"; - case MG_EV_MQTT_SUBACK: - return "MG_EV_MQTT_SUBACK"; - case MG_EV_MQTT_SUBSCRIBE: - return "MG_EV_MQTT_SUBSCRIBE"; - case MG_EV_MQTT_UNSUBACK: - return "MG_EV_MQTT_UNSUBACK"; - case MG_EV_MQTT_UNSUBSCRIBE: - return "MG_EV_MQTT_UNSUBSCRIBE"; - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: - return "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"; - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: - return "MG_EV_WEBSOCKET_HANDSHAKE_DONE"; - case MG_EV_WEBSOCKET_FRAME: - return "MG_EV_WEBSOCKET_FRAME"; - case MG_EV_WEBSOCKET_CONTROL_FRAME: - return "MG_EV_WEBSOCKET_CONTROL_FRAME"; - } - std::string s; - s += "Unknown event: "; - s += event; - return s; + switch (event) { + case MG_EV_CONNECT: + return "MG_EV_CONNECT"; + case MG_EV_ACCEPT: + return "MG_EV_ACCEPT"; + case MG_EV_CLOSE: + return "MG_EV_CLOSE"; + case MG_EV_SEND: + return "MG_EV_SEND"; + case MG_EV_RECV: + return "MG_EV_RECV"; + case MG_EV_POLL: + return "MG_EV_POLL"; + case MG_EV_TIMER: + return "MG_EV_TIMER"; + case MG_EV_HTTP_PART_DATA: + return "MG_EV_HTTP_PART_DATA"; + case MG_EV_HTTP_MULTIPART_REQUEST: + return "MG_EV_HTTP_MULTIPART_REQUEST"; + case MG_EV_HTTP_PART_BEGIN: + return "MG_EV_HTTP_PART_BEGIN"; + case MG_EV_HTTP_PART_END: + return "MG_EV_HTTP_PART_END"; + case MG_EV_HTTP_MULTIPART_REQUEST_END: + return "MG_EV_HTTP_MULTIPART_REQUEST_END"; + case MG_EV_HTTP_REQUEST: + return "MG_EV_HTTP_REQUEST"; + case MG_EV_HTTP_REPLY: + return "MG_EV_HTTP_REPLY"; + case MG_EV_HTTP_CHUNK: + return "MG_EV_HTTP_CHUNK"; + case MG_EV_MQTT_CONNACK: + return "MG_EV_MQTT_CONNACK"; + case MG_EV_MQTT_CONNECT: + return "MG_EV_MQTT_CONNECT"; + case MG_EV_MQTT_DISCONNECT: + return "MG_EV_MQTT_DISCONNECT"; + case MG_EV_MQTT_PINGREQ: + return "MG_EV_MQTT_PINGREQ"; + case MG_EV_MQTT_PINGRESP: + return "MG_EV_MQTT_PINGRESP"; + case MG_EV_MQTT_PUBACK: + return "MG_EV_MQTT_PUBACK"; + case MG_EV_MQTT_PUBCOMP: + return "MG_EV_MQTT_PUBCOMP"; + case MG_EV_MQTT_PUBLISH: + return "MG_EV_MQTT_PUBLISH"; + case MG_EV_MQTT_PUBREC: + return "MG_EV_MQTT_PUBREC"; + case MG_EV_MQTT_PUBREL: + return "MG_EV_MQTT_PUBREL"; + case MG_EV_MQTT_SUBACK: + return "MG_EV_MQTT_SUBACK"; + case MG_EV_MQTT_SUBSCRIBE: + return "MG_EV_MQTT_SUBSCRIBE"; + case MG_EV_MQTT_UNSUBACK: + return "MG_EV_MQTT_UNSUBACK"; + case MG_EV_MQTT_UNSUBSCRIBE: + return "MG_EV_MQTT_UNSUBSCRIBE"; + case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: + return "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"; + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: + return "MG_EV_WEBSOCKET_HANDSHAKE_DONE"; + case MG_EV_WEBSOCKET_FRAME: + return "MG_EV_WEBSOCKET_FRAME"; + case MG_EV_WEBSOCKET_CONTROL_FRAME: + return "MG_EV_WEBSOCKET_CONTROL_FRAME"; + } + std::string s; + s += "Unknown event: "; + s += event; + return s; } //eventToString -static void dumpHttpMessage(struct http_message *pHttpMessage) { - ESP_LOGD(tag, "HTTP Message"); - ESP_LOGD(tag, "Message: %.*s", (int)pHttpMessage->message.len, pHttpMessage->message.p); + +static void dumpHttpMessage(struct http_message* pHttpMessage) { + ESP_LOGD(LOG_TAG, "HTTP Message"); + ESP_LOGD(LOG_TAG, "Message: %.*s", (int) pHttpMessage->message.len, pHttpMessage->message.p); } /* -static struct mg_str uploadFileNameHandler(struct mg_connection *mgConnection, struct mg_str fname) { - ESP_LOGD(tag, "uploadFileNameHandler: %s", mgStrToString(fname).c_str()); - return fname; +static struct mg_str uploadFileNameHandler(struct mg_connection* mgConnection, struct mg_str fname) { + ESP_LOGD(LOG_TAG, "uploadFileNameHandler: %s", mgStrToString(fname).c_str()); + return fname; } */ + /** * @brief Mongoose event handler. * The event handler is called when an event occurs associated with the WebServer @@ -129,147 +135,139 @@ static struct mg_str uploadFileNameHandler(struct mg_connection *mgConnection, s * @param [in] eventData Data associated with the event. * @return N/A. */ -static void mongoose_event_handler_web_server( - struct mg_connection *mgConnection, // The network connection associated with the event. - int event, // The type of event. - void *eventData // Data associated with the event. -) { - if (event == MG_EV_POLL) { - return; - } - ESP_LOGD(tag, "Event: %s [%d]", mongoose_eventToString(event).c_str(), mgConnection->sock); - switch (event) { - case MG_EV_SEND: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - pWebServer->continueConnection(mgConnection); - break; - } - - case MG_EV_HTTP_REQUEST: { - struct http_message *message = (struct http_message *) eventData; - dumpHttpMessage(message); - - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - pWebServer->processRequest(mgConnection, message); - break; - } // MG_EV_HTTP_REQUEST - - case MG_EV_HTTP_MULTIPART_REQUEST: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - ESP_LOGD(tag, "User_data address 0x%d", (uint32_t)pWebServerUserData); - WebServer *pWebServer = pWebServerUserData->pWebServer; - if (pWebServer->m_pMultiPartFactory == nullptr) { - return; - } - WebServer::HTTPMultiPart *pMultiPart = pWebServer->m_pMultiPartFactory->newInstance(); - struct WebServerUserData *p2 = new WebServerUserData(); - ESP_LOGD(tag, "New User_data address 0x%d", (uint32_t)p2); - p2->originalUserData = pWebServerUserData; - p2->pWebServer = pWebServerUserData->pWebServer; - p2->pMultiPart = pMultiPart; - p2->pWebSocketHandler = nullptr; - mgConnection->user_data = p2; - //struct http_message *message = (struct http_message *) eventData; - //dumpHttpMessage(message); - break; - } // MG_EV_HTTP_MULTIPART_REQUEST - - case MG_EV_HTTP_MULTIPART_REQUEST_END: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pMultiPart != nullptr) { - delete pWebServerUserData->pMultiPart; - pWebServerUserData->pMultiPart = nullptr; - } - mgConnection->user_data = pWebServerUserData->originalUserData; - delete pWebServerUserData; - WebServer::HTTPResponse httpResponse = WebServer::HTTPResponse(mgConnection); - httpResponse.setStatus(200); - httpResponse.sendData(""); - break; - } // MG_EV_HTTP_MULTIPART_REQUEST_END - - case MG_EV_HTTP_PART_BEGIN: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->begin(std::string(part->var_name), std::string(part->file_name)); - } - break; - } // MG_EV_HTTP_PART_BEGIN - - case MG_EV_HTTP_PART_DATA: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->data(std::string(part->data.p, part->data.len)); - } - break; - } // MG_EV_HTTP_PART_DATA - - case MG_EV_HTTP_PART_END: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->end(); - } - break; - } // MG_EV_HTTP_PART_END - - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - if (pWebServer->m_pWebSocketHandlerFactory != nullptr) { - if (pWebServerUserData->pWebSocketHandler != nullptr) { - ESP_LOGD(tag, "Warning: MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: pWebSocketHandler was NOT null"); - } - struct WebServerUserData *p2 = new WebServerUserData(); - ESP_LOGD(tag, "New User_data address 0x%d", (uint32_t)p2); - p2->originalUserData = pWebServerUserData; - p2->pWebServer = pWebServerUserData->pWebServer; - p2->pWebSocketHandler = pWebServer->m_pWebSocketHandlerFactory->newInstance(); - mgConnection->user_data = p2; - } else { - ESP_LOGD(tag, "We received a WebSocket request but we have no handler factory!"); - } - break; - } // MG_EV_WEBSOCKET_HANDSHAKE_REQUEST - - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pWebSocketHandler == nullptr) { - ESP_LOGE(tag, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); - return; - } - pWebServerUserData->pWebSocketHandler->onCreated(); - break; - } // MG_EV_WEBSOCKET_HANDSHAKE_DONE - - - /* - * When we receive a MG_EV_WEBSOCKET_FRAME then we have received a chunk of data over the network. - * Our goal will be to send this to the web socket handler (if one exists). - */ - case MG_EV_WEBSOCKET_FRAME: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pWebSocketHandler == nullptr) { - ESP_LOGE(tag, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); - return; - } - struct websocket_message *ws_message = (websocket_message *)eventData; - ESP_LOGD(tag, "Received data length: %d", ws_message->size); - pWebServerUserData->pWebSocketHandler->onMessage(std::string((char *)ws_message->data, ws_message->size)); - break; - } // MG_EV_WEBSOCKET_FRAME - - } // End of switch +static void mongoose_event_handler_web_server(struct mg_connection* mgConnection, int event, void* eventData) { + if (event == MG_EV_POLL) return; + ESP_LOGD(LOG_TAG, "Event: %s [%d]", mongoose_eventToString(event).c_str(), mgConnection->sock); + switch (event) { + case MG_EV_SEND: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + pWebServer->continueConnection(mgConnection); + break; + } + + case MG_EV_HTTP_REQUEST: { + struct http_message* message = (struct http_message*) eventData; + dumpHttpMessage(message); + + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + pWebServer->processRequest(mgConnection, message); + break; + } // MG_EV_HTTP_REQUEST + + case MG_EV_HTTP_MULTIPART_REQUEST: { + struct WebServerUserData *pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + ESP_LOGD(LOG_TAG, "User_data address 0x%d", (uint32_t) pWebServerUserData); + WebServer* pWebServer = pWebServerUserData->pWebServer; + if (pWebServer->m_pMultiPartFactory == nullptr) return; + WebServer::HTTPMultiPart* pMultiPart = pWebServer->m_pMultiPartFactory->newInstance(); + struct WebServerUserData* p2 = new WebServerUserData(); + ESP_LOGD(LOG_TAG, "New User_data address 0x%d", (uint32_t) p2); + p2->originalUserData = pWebServerUserData; + p2->pWebServer = pWebServerUserData->pWebServer; + p2->pMultiPart = pMultiPart; + p2->pWebSocketHandler = nullptr; + mgConnection->user_data = p2; + //struct http_message* message = (struct http_message*) eventData; + //dumpHttpMessage(message); + break; + } // MG_EV_HTTP_MULTIPART_REQUEST + + case MG_EV_HTTP_MULTIPART_REQUEST_END: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pMultiPart != nullptr) { + delete pWebServerUserData->pMultiPart; + pWebServerUserData->pMultiPart = nullptr; + } + mgConnection->user_data = pWebServerUserData->originalUserData; + delete pWebServerUserData; + WebServer::HTTPResponse httpResponse = WebServer::HTTPResponse(mgConnection); + httpResponse.setStatus(200); + httpResponse.sendData(""); + break; + } // MG_EV_HTTP_MULTIPART_REQUEST_END + + case MG_EV_HTTP_PART_BEGIN: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t) part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->begin(std::string(part->var_name), std::string(part->file_name)); + } + break; + } // MG_EV_HTTP_PART_BEGIN + + case MG_EV_HTTP_PART_DATA: { + struct WebServerUserData *pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t) part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->data(std::string(part->data.p, part->data.len)); + } + break; + } // MG_EV_HTTP_PART_DATA + + case MG_EV_HTTP_PART_END: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t)part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->end(); + } + break; + } // MG_EV_HTTP_PART_END + + case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + if (pWebServer->m_pWebSocketHandlerFactory != nullptr) { + if (pWebServerUserData->pWebSocketHandler != nullptr) { + ESP_LOGD(LOG_TAG, "Warning: MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: pWebSocketHandler was NOT null"); + } + struct WebServerUserData* p2 = new WebServerUserData(); + ESP_LOGD(LOG_TAG, "New User_data address 0x%d", (uint32_t) p2); + p2->originalUserData = pWebServerUserData; + p2->pWebServer = pWebServerUserData->pWebServer; + p2->pWebSocketHandler = pWebServer->m_pWebSocketHandlerFactory->newInstance(); + mgConnection->user_data = p2; + } else { + ESP_LOGD(LOG_TAG, "We received a WebSocket request but we have no handler factory!"); + } + break; + } // MG_EV_WEBSOCKET_HANDSHAKE_REQUEST + + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pWebSocketHandler == nullptr) { + ESP_LOGE(LOG_TAG, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); + return; + } + pWebServerUserData->pWebSocketHandler->onCreated(); + break; + } // MG_EV_WEBSOCKET_HANDSHAKE_DONE + + + /* + * When we receive a MG_EV_WEBSOCKET_FRAME then we have received a chunk of data over the network. + * Our goal will be to send this to the web socket handler (if one exists). + */ + case MG_EV_WEBSOCKET_FRAME: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pWebSocketHandler == nullptr) { + ESP_LOGE(LOG_TAG, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); + return; + } + struct websocket_message* ws_message = (websocket_message*) eventData; + ESP_LOGD(LOG_TAG, "Received data length: %d", ws_message->size); + pWebServerUserData->pWebSocketHandler->onMessage(std::string((char*) ws_message->data, ws_message->size)); + break; + } // MG_EV_WEBSOCKET_FRAME + + } // End of switch } // End of mongoose_event_handler @@ -277,9 +275,9 @@ static void mongoose_event_handler_web_server( * @brief Constructor. */ WebServer::WebServer() { - m_rootPath = ""; - m_pMultiPartFactory = nullptr; - m_pWebSocketHandlerFactory = nullptr; + m_rootPath = ""; + m_pMultiPartFactory = nullptr; + m_pWebSocketHandlerFactory = nullptr; } // WebServer @@ -292,7 +290,7 @@ WebServer::~WebServer() { * @return The current root path. */ const std::string& WebServer::getRootPath() { - return m_rootPath; + return m_rootPath; } // getRootPath @@ -317,15 +315,17 @@ const std::string& WebServer::getRootPath() { * @param [in] handler The callback function to be invoked when a request arrives. */ void WebServer::addPathHandler(const std::string& method, const std::string& pathExpr, - void (* handler)(WebServer::HTTPRequest* pHttpRequest, + void (*handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); + m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); } // addPathHandler -void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, void (* handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_pathHandlers.push_back(PathHandler(std::move(method), pathExpr, handler)); + +void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, void (*handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_pathHandlers.push_back(PathHandler(std::move(method), pathExpr, handler)); } // addPathHandler + /** * @brief Run the web server listening at the given port. * @@ -335,31 +335,31 @@ void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr * @return N/A. */ void WebServer::start(uint16_t port) { - ESP_LOGD(tag, "WebServer task starting"); - struct mg_mgr mgr; - mg_mgr_init(&mgr, NULL); - - std::stringstream stringStream; - stringStream << ':' << port; - struct mg_connection *mgConnection = mg_bind(&mgr, stringStream.str().c_str(), mongoose_event_handler_web_server); - - if (mgConnection == NULL) { - ESP_LOGE(tag, "No connection from the mg_bind()"); - vTaskDelete(NULL); - return; - } - - struct WebServerUserData *pWebServerUserData = new WebServerUserData(); - pWebServerUserData->pWebServer = this; - pWebServerUserData->pMultiPart = nullptr; - mgConnection->user_data = pWebServerUserData; // Save the WebServer instance reference in user_data. - ESP_LOGD(tag, "start: User_data address 0x%d", (uint32_t)pWebServerUserData); - mg_set_protocol_http_websocket(mgConnection); - - ESP_LOGD(tag, "WebServer listening on port %d", port); - while (1) { - mg_mgr_poll(&mgr, 2000); - } + ESP_LOGD(LOG_TAG, "WebServer task starting"); + struct mg_mgr mgr; + mg_mgr_init(&mgr, NULL); + + std::stringstream stringStream; + stringStream << ':' << port; + struct mg_connection *mgConnection = mg_bind(&mgr, stringStream.str().c_str(), mongoose_event_handler_web_server); + + if (mgConnection == NULL) { + ESP_LOGE(LOG_TAG, "No connection from the mg_bind()"); + vTaskDelete(NULL); + return; + } + + struct WebServerUserData* pWebServerUserData = new WebServerUserData(); + pWebServerUserData->pWebServer = this; + pWebServerUserData->pMultiPart = nullptr; + mgConnection->user_data = pWebServerUserData; // Save the WebServer instance reference in user_data. + ESP_LOGD(LOG_TAG, "start: User_data address 0x%d", (uint32_t)pWebServerUserData); + mg_set_protocol_http_websocket(mgConnection); + + ESP_LOGD(LOG_TAG, "WebServer listening on port %d", port); + while (true) { + mg_mgr_poll(&mgr, 2000); + } } // run @@ -368,7 +368,7 @@ void WebServer::start(uint16_t port) { * @param [in] pMultiPart A pointer to the multi part factory. */ void WebServer::setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory) { - m_pMultiPartFactory = pMultiPartFactory; + m_pMultiPartFactory = pMultiPartFactory; } @@ -390,11 +390,12 @@ void WebServer::setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory) { * @return N/A. */ void WebServer::setRootPath(const std::string& path) { - m_rootPath = path; + m_rootPath = path; } // setRootPath + void WebServer::setRootPath(std::string&& path) { - m_rootPath = std::move(path); + m_rootPath = std::move(path); } // setRootPath @@ -405,7 +406,7 @@ void WebServer::setRootPath(std::string&& path) { * @return N/A. */ void WebServer::setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHandlerFactory) { - m_pWebSocketHandlerFactory = pWebSocketHandlerFactory; + m_pWebSocketHandlerFactory = pWebSocketHandlerFactory; } // setWebSocketHandlerFactory @@ -414,9 +415,9 @@ void WebServer::setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHa * @param [in] nc The network connection for the response. */ WebServer::HTTPResponse::HTTPResponse(struct mg_connection* nc) { - m_nc = nc; - m_status = 200; - m_dataSent = false; + m_nc = nc; + m_status = 200; + m_dataSent = false; } // HTTPResponse @@ -426,11 +427,11 @@ WebServer::HTTPResponse::HTTPResponse(struct mg_connection* nc) { * @param [in] value The value of the header. */ void WebServer::HTTPResponse::addHeader(const std::string& name, const std::string& value) { - m_headers[name] = value; + m_headers[name] = value; } // addHeader void WebServer::HTTPResponse::addHeader(std::string&& name, std::string&& value) { - m_headers[std::move(name)] = std::move(value); + m_headers[std::move(name)] = std::move(value); } // addHeader @@ -442,9 +443,10 @@ std::string WebServer::HTTPResponse::buildHeaders() { std::string headers; unsigned long headers_len = 0; - for(auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers_len += 2; + for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { + if (iter != m_headers.begin()) { + headers_len += 2; + } headers_len += iter->first.length(); headers_len += 2; headers_len += iter->second.length(); @@ -453,8 +455,9 @@ std::string WebServer::HTTPResponse::buildHeaders() { headers.resize(headers_len); // Will not have to resize and recopy during the next loop, we have 2 loops but it still ends up being faster for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers += "\r\n"; + if (iter != m_headers.begin()) { + headers += "\r\n"; + } headers += iter->first; headers += ": "; headers += iter->second; @@ -462,6 +465,7 @@ std::string WebServer::HTTPResponse::buildHeaders() { return headers; } // buildHeaders + /** * @brief Send data to the HTTP caller. * Send the data to the HTTP caller. No further data should be sent after this call. @@ -469,13 +473,10 @@ std::string WebServer::HTTPResponse::buildHeaders() { * @return N/A. */ void WebServer::HTTPResponse::sendData(const std::string& data) { - sendData((uint8_t *)data.data(), data.length()); + sendData((uint8_t*) data.data(), data.length()); } // sendData - - - /** * @brief Send data to the HTTP caller. * Send the data to the HTTP caller. No further data should be sent after this call. @@ -484,15 +485,15 @@ void WebServer::HTTPResponse::sendData(const std::string& data) { * @return N/A. */ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { - if (m_dataSent) { - ESP_LOGE(tag, "HTTPResponse: Data already sent! Attempt to send again/more."); - return; - } - m_dataSent = true; - - mg_send_head(m_nc, m_status, length, buildHeaders().c_str()); - mg_send(m_nc, pData, length); - m_nc->flags |= MG_F_SEND_AND_CLOSE; + if (m_dataSent) { + ESP_LOGE(LOG_TAG, "HTTPResponse: Data already sent! Attempt to send again/more."); + return; + } + m_dataSent = true; + + mg_send_head(m_nc, m_status, length, buildHeaders().c_str()); + mg_send(m_nc, pData, length); + m_nc->flags |= MG_F_SEND_AND_CLOSE; } // sendData @@ -500,7 +501,7 @@ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { * */ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { - sendData((uint8_t*) pData, length); + sendData((uint8_t*) pData, length); } // sendData @@ -508,11 +509,11 @@ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { * */ void WebServer::HTTPResponse::sendChunkHead() { - if(m_dataSent) { - ESP_LOGE(tag, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); - } - m_dataSent = true; - mg_send_head(m_nc, m_status, -1, buildHeaders().c_str()); + if (m_dataSent) { + ESP_LOGE(LOG_TAG, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); + } + m_dataSent = true; + mg_send_head(m_nc, m_status, -1, buildHeaders().c_str()); } // sendChunkHead @@ -520,7 +521,7 @@ void WebServer::HTTPResponse::sendChunkHead() { * */ void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { - mg_send_http_chunk(m_nc, pData, length); + mg_send_http_chunk(m_nc, pData, length); } // sendChunkHead @@ -528,7 +529,7 @@ void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { * */ void WebServer::HTTPResponse::closeConnection() { - m_nc->flags |= MG_F_SEND_AND_CLOSE; + m_nc->flags |= MG_F_SEND_AND_CLOSE; } // closeConnection @@ -538,11 +539,12 @@ void WebServer::HTTPResponse::closeConnection() { * @return N/A. */ void WebServer::HTTPResponse::setHeaders(const std::map& headers) { - m_headers = headers; + m_headers = headers; } // setHeaders + void WebServer::HTTPResponse::setHeaders(std::map&& headers) { - m_headers = std::move(headers); + m_headers = std::move(headers); } // setHeaders @@ -551,7 +553,7 @@ void WebServer::HTTPResponse::setHeaders(std::map&& he * @return The current root path. */ const std::string& WebServer::HTTPResponse::getRootPath() const { - return m_rootPath; + return m_rootPath; } // getRootPath @@ -561,13 +563,15 @@ const std::string& WebServer::HTTPResponse::getRootPath() const { * @return N/A. */ void WebServer::HTTPResponse::setRootPath(const std::string& path) { - m_rootPath = path; + m_rootPath = path; } // setRootPath + void WebServer::HTTPResponse::setRootPath(std::string&& path) { - m_rootPath = std::move(path); + m_rootPath = std::move(path); } // setRootPath + /** * @brief Set the status value in the HTTP response. * @@ -576,7 +580,7 @@ void WebServer::HTTPResponse::setRootPath(std::string&& path) { * @return N/A. */ void WebServer::HTTPResponse::setStatus(int status) { - m_status = status; + m_status = status; } // setStatus @@ -590,74 +594,73 @@ void WebServer::HTTPResponse::setStatus(int status) { * @param [in] mgConnection The network connection on which the request was received. * @param [in] message The message representing the request. */ -void WebServer::processRequest(struct mg_connection *mgConnection, struct http_message* message) { - ESP_LOGD(tag, "WebServer::processRequest: Matching: %.*s", (int)message->uri.len, message->uri.p); - HTTPResponse httpResponse = HTTPResponse(mgConnection); - - /* - * Iterate through each of the path handlers looking for a match with the method and specified path. - */ - std::vector::iterator it; - for (it = m_pathHandlers.begin(); it != m_pathHandlers.end(); ++it) { - if ((*it).match(message->method.p, message->method.len, message->uri.p)) { - HTTPRequest httpRequest(message); - (*it).invoke(&httpRequest, &httpResponse); - ESP_LOGD(tag, "Found a match!!"); - return; - } - } // End of examine path handlers. - - // Because we reached here, it means that we did NOT match a handler. Now we want to attempt - // to retrieve the corresponding file content. - std::string filePath; - filePath.reserve(httpResponse.getRootPath().length() + message->uri.len + 1); - filePath += httpResponse.getRootPath(); - filePath.append(message->uri.p, message->uri.len); - ESP_LOGD(tag, "Opening file: %s", filePath.c_str()); - FILE* file = nullptr; - - if(strcmp(filePath.c_str(), "/") != 0) - file = fopen(filePath.c_str(), "rb"); - if (file != nullptr) { - auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); - size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); - - if(read >= MAX_CHUNK_LENGTH) { - httpResponse.sendChunkHead(); - httpResponse.sendChunk((char*)pData, read); - fclose(unfinishedConnection[mgConnection->sock]); - unfinishedConnection[mgConnection->sock] = file; - } else { - fclose(file); - httpResponse.sendData(pData, read); - } - free(pData); - } else { - // Handle unable to open file - httpResponse.setStatus(404); // Not found - httpResponse.sendData(""); - } +void WebServer::processRequest(struct mg_connection* mgConnection, struct http_message* message) { + ESP_LOGD(LOG_TAG, "WebServer::processRequest: Matching: %.*s", (int) message->uri.len, message->uri.p); + HTTPResponse httpResponse = HTTPResponse(mgConnection); + + /* + * Iterate through each of the path handlers looking for a match with the method and specified path. + */ + std::vector::iterator it; + for (it = m_pathHandlers.begin(); it != m_pathHandlers.end(); ++it) { + if ((*it).match(message->method.p, message->method.len, message->uri.p)) { + HTTPRequest httpRequest(message); + (*it).invoke(&httpRequest, &httpResponse); + ESP_LOGD(LOG_TAG, "Found a match!!"); + return; + } + } // End of examine path handlers. + + // Because we reached here, it means that we did NOT match a handler. Now we want to attempt + // to retrieve the corresponding file content. + std::string filePath; + filePath.reserve(httpResponse.getRootPath().length() + message->uri.len + 1); + filePath += httpResponse.getRootPath(); + filePath.append(message->uri.p, message->uri.len); + ESP_LOGD(LOG_TAG, "Opening file: %s", filePath.c_str()); + FILE* file = nullptr; + + if (strcmp(filePath.c_str(), "/") != 0) { + file = fopen(filePath.c_str(), "rb"); + } + if (file != nullptr) { + auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); + size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); + + if (read >= MAX_CHUNK_LENGTH) { + httpResponse.sendChunkHead(); + httpResponse.sendChunk((char*) pData, read); + fclose(unfinishedConnection[mgConnection->sock]); + unfinishedConnection[mgConnection->sock] = file; + } else { + fclose(file); + httpResponse.sendData(pData, read); + } + free(pData); + } else { + // Handle unable to open file + httpResponse.setStatus(404); // Not found + httpResponse.sendData(""); + } } // processRequest void WebServer::continueConnection(struct mg_connection* mgConnection) { - if(unfinishedConnection.count(mgConnection->sock) == 0) { - return; - } - - HTTPResponse httpResponse = HTTPResponse(mgConnection); - - FILE* file = unfinishedConnection[mgConnection->sock]; - auto pData = (char*) malloc(MAX_CHUNK_LENGTH); - size_t length = fread(pData, 1, MAX_CHUNK_LENGTH, file); - - httpResponse.sendChunk(pData, length); - if(length < MAX_CHUNK_LENGTH) { - fclose(file); - httpResponse.closeConnection(); - unfinishedConnection.erase(mgConnection->sock); - httpResponse.sendChunk("", 0); - } - free(pData); + if (unfinishedConnection.count(mgConnection->sock) == 0) return; + + HTTPResponse httpResponse = HTTPResponse(mgConnection); + + FILE* file = unfinishedConnection[mgConnection->sock]; + auto pData = (char*) malloc(MAX_CHUNK_LENGTH); + size_t length = fread(pData, 1, MAX_CHUNK_LENGTH, file); + + httpResponse.sendChunk(pData, length); + if (length < MAX_CHUNK_LENGTH) { + fclose(file); + httpResponse.closeConnection(); + unfinishedConnection.erase(mgConnection->sock); + httpResponse.sendChunk("", 0); + } + free(pData); } @@ -668,16 +671,16 @@ void WebServer::continueConnection(struct mg_connection* mgConnection) { * @param [in] pathPattern The path pattern to be matched. * @param [in] webServerRequestHandler The request handler to be called. */ -WebServer::PathHandler::PathHandler(const std::string& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_method = method; - m_pattern = std::regex(pathPattern); - m_requestHandler = webServerRequestHandler; +WebServer::PathHandler::PathHandler(const std::string& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_method = method; + m_pattern = std::regex(pathPattern); + m_requestHandler = webServerRequestHandler; } // PathHandler -WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_method = std::move(method); - m_pattern = std::regex(pathPattern); - m_requestHandler = webServerRequestHandler; +WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_method = std::move(method); + m_pattern = std::regex(pathPattern); + m_requestHandler = webServerRequestHandler; } // PathHandler @@ -689,13 +692,12 @@ WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pat * @param [in] path The path to be matched. * @return True if the path matches. */ - bool WebServer::PathHandler::match(const char* method, size_t method_len, const char* path) { - //ESP_LOGD(tag, "match: %s with %s", m_pattern.c_str(), path.c_str()); - if (method_len != m_method.length() || strncmp(method, m_method.c_str(), method_len) != 0) { - return false; - } - return std::regex_search(path, m_pattern); + //ESP_LOGD(LOG_TAG, "match: %s with %s", m_pattern.c_str(), path.c_str()); + if (method_len != m_method.length() || strncmp(method, m_method.c_str(), method_len) != 0) { + return false; + } + return std::regex_search(path, m_pattern); } // match @@ -705,8 +707,8 @@ bool WebServer::PathHandler::match(const char* method, size_t method_len, const * @param [in] response An object representing the response. * @return N/A. */ -void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer::HTTPResponse *response) { - m_requestHandler(request, response); +void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer::HTTPResponse* response) { + m_requestHandler(request, response); } // invoke @@ -717,7 +719,7 @@ void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer:: * @param [in] message The description of the underlying Mongoose message. */ WebServer::HTTPRequest::HTTPRequest(struct http_message* message) { - m_message = message; + m_message = message; } // HTTPRequest @@ -729,9 +731,10 @@ WebServer::HTTPRequest::HTTPRequest(struct http_message* message) { * @return The body of the request. */ const char* WebServer::HTTPRequest::getBody() const { - return m_message->body.p; + return m_message->body.p; } // getBody + /** * @brief Get the length of the body of the request. * When an HTTP request is either PUT or POST then it may contain a payload that is also @@ -739,7 +742,7 @@ const char* WebServer::HTTPRequest::getBody() const { * @return The length of the body of the request. */ size_t WebServer::HTTPRequest::getBodyLen() const { - return m_message->body.len; + return m_message->body.len; } // getBodyLen @@ -751,16 +754,17 @@ size_t WebServer::HTTPRequest::getBodyLen() const { * @return The body of the request. */ const char* WebServer::HTTPRequest::getMethod() const { - return m_message->method.p; + return m_message->method.p; } // getMethod + /** * @brief Get the length of the method of the request. * An HTTP request contains a request method which is one of GET, PUT, POST, etc. * @return The length of the method of the request. */ size_t WebServer::HTTPRequest::getMethodLen() const { - return m_message->method.len; + return m_message->method.len; } // getMethodLen @@ -773,9 +777,10 @@ size_t WebServer::HTTPRequest::getMethodLen() const { * @return The path of the request. */ const char* WebServer::HTTPRequest::getPath() const { - return m_message->uri.p; + return m_message->uri.p; } // getPath + /** * @brief Get the path of the request. * The path of an HTTP request is the portion of the URL that follows the hostname/port pair @@ -783,11 +788,10 @@ const char* WebServer::HTTPRequest::getPath() const { * @return The path of the request. */ size_t WebServer::HTTPRequest::getPathLen() const { - return m_message->uri.len; + return m_message->uri.len; } // getPath -#define STATE_NAME 0 -#define STATE_VALUE 1 + /** * @brief Get the query part of the request. * The query is a set of name = value pairs. The return is a map keyed by the name items. @@ -795,48 +799,47 @@ size_t WebServer::HTTPRequest::getPathLen() const { * @return The query part of the request. */ std::map WebServer::HTTPRequest::getQuery() const { - // Walk through all the characters in the query string maintaining a simple state machine - // that lets us know what we are parsing. - std::map queryMap; - const char* queryString = m_message->query_string.p; - size_t queryStringLen = m_message->query_string.len; - int i=0; - - /* - * We maintain a simple state machine with states of: - * * STATE_NAME - We are parsing a name. - * * STATE_VALUE - We are parsing a value. - */ - int state = STATE_NAME; - std::string name = ""; - std::string value; - // Loop through each character in the query string. - for (i=0; i queryMap; + const char* queryString = m_message->query_string.p; + size_t queryStringLen = m_message->query_string.len; + + /* + * We maintain a simple state machine with states of: + * * STATE_NAME - We are parsing a name. + * * STATE_VALUE - We are parsing a value. + */ + int state = STATE_NAME; + std::string name = ""; + std::string value; + // Loop through each character in the query string. + for (size_t i = 0; i < queryStringLen; i++) { + char currentChar = queryString[i]; + if (state == STATE_NAME) { + if (currentChar != '=') { + name += currentChar; + } else { + state = STATE_VALUE; + value = ""; + } + } // End state = STATE_NAME + else { // if (state == STATE_VALUE) + if (currentChar != '&') { + value += currentChar; + } else { + //ESP_LOGD(LOG_TAG, "name=%s, value=%s", name.c_str(), value.c_str()); + queryMap[name] = value; + state = STATE_NAME; + name = ""; + } + } // End state = STATE_VALUE + } // End for loop + if (state == STATE_VALUE) { + //ESP_LOGD(LOG_TAG, "name=%s, value=%s", name.c_str(), value.c_str()); + queryMap[name] = value; + } + return queryMap; } // getQuery @@ -860,17 +863,17 @@ std::map WebServer::HTTPRequest::getQuery() const { * @return A vector of the constituent parts of the path. */ std::vector WebServer::HTTPRequest::pathSplit() const { - std::istringstream stream(std::string(getPath(), getPathLen())); // I don't know if there's a better istringstream constructor for this - std::vector ret; - std::string pathPart; - while(std::getline(stream, pathPart, '/')) { - ret.push_back(pathPart); - } - // Debug - for (int i=0; i ret; + std::string pathPart; + while (std::getline(stream, pathPart, '/')) { + ret.push_back(pathPart); + } + // Debug + for (int i = 0; i < ret.size(); i++) { + ESP_LOGD(LOG_TAG, "part[%d]: %s", i, ret[i].c_str()); + } + return ret; } // pathSplit /** @@ -882,8 +885,8 @@ std::vector WebServer::HTTPRequest::pathSplit() const { * @return N/A. */ void WebServer::HTTPMultiPart::begin(const std::string& varName, const std::string& fileName) { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::begin(varName=\"%s\", fileName=\"%s\")", - varName.c_str(), fileName.c_str()); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::begin(varName=\"%s\", fileName=\"%s\")", + varName.c_str(), fileName.c_str()); } // WebServer::HTTPMultiPart::begin @@ -893,7 +896,7 @@ void WebServer::HTTPMultiPart::begin(const std::string& varName, const std::stri * @return N/A. */ void WebServer::HTTPMultiPart::end() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::end()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::end()"); } // WebServer::HTTPMultiPart::end @@ -905,7 +908,7 @@ void WebServer::HTTPMultiPart::end() { * @return N/A. */ void WebServer::HTTPMultiPart::data(const std::string& data) { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::data(), length=%d", data.length()); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::data(), length=%d", data.length()); } // WebServer::HTTPMultiPart::data @@ -914,7 +917,7 @@ void WebServer::HTTPMultiPart::data(const std::string& data) { * @return N/A. */ void WebServer::HTTPMultiPart::multipartEnd() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::multipartEnd()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::multipartEnd()"); } // WebServer::HTTPMultiPart::multipartEnd @@ -923,7 +926,7 @@ void WebServer::HTTPMultiPart::multipartEnd() { * @return N/A. */ void WebServer::HTTPMultiPart::multipartStart() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::multipartStart()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::multipartStart()"); } // WebServer::HTTPMultiPart::multipartStart @@ -932,7 +935,6 @@ void WebServer::HTTPMultiPart::multipartStart() { * @return N/A. */ void WebServer::WebSocketHandler::onCreated() { - } // onCreated @@ -942,7 +944,6 @@ void WebServer::WebSocketHandler::onCreated() { * @return N/A. */ void WebServer::WebSocketHandler::onMessage(const std::string& message){ - } // onMessage /** @@ -950,7 +951,6 @@ void WebServer::WebSocketHandler::onMessage(const std::string& message){ * @return N/A */ void WebServer::WebSocketHandler::onClosed() { - } // onClosed @@ -960,12 +960,13 @@ void WebServer::WebSocketHandler::onClosed() { * @return N/A. */ void WebServer::WebSocketHandler::sendData(const std::string& message) { - ESP_LOGD(tag, "WebSocketHandler::sendData(length=%d)", message.length()); - mg_send_websocket_frame(m_mgConnection, - WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, - message.data(), message.length()); + ESP_LOGD(LOG_TAG, "WebSocketHandler::sendData(length=%d)", message.length()); + mg_send_websocket_frame(m_mgConnection, + WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, + message.data(), message.length()); } // sendData + /** * @brief Send data down the WebSocket * @param [in] data The message to send down the socket. @@ -973,9 +974,9 @@ void WebServer::WebSocketHandler::sendData(const std::string& message) { * @return N/A. */ void WebServer::WebSocketHandler::sendData(const uint8_t* data, uint32_t size) { - mg_send_websocket_frame(m_mgConnection, - WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, - data, size); + mg_send_websocket_frame(m_mgConnection, + WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, + data, size); } // sendData @@ -986,9 +987,7 @@ void WebServer::WebSocketHandler::sendData(const uint8_t* data, uint32_t size) { * @return N/A. */ void WebServer::WebSocketHandler::close() { - mg_send_websocket_frame(m_mgConnection, WEBSOCKET_OP_CLOSE, nullptr, 0); + mg_send_websocket_frame(m_mgConnection, WEBSOCKET_OP_CLOSE, nullptr, 0); } // close - - #endif // CONFIG_MONGOOSE_PRESENT diff --git a/cpp_utils/WebServer.h b/cpp_utils/WebServer.h index ae06647b..961b6662 100644 --- a/cpp_utils/WebServer.h +++ b/cpp_utils/WebServer.h @@ -27,183 +27,196 @@ class WebServer; */ class WebServer { public: - /** - * @brief Request wrapper for an HTTP request. - */ - class HTTPRequest { - public: - HTTPRequest(struct http_message* message); - const char* getMethod() const; - const char* getPath() const; - const char* getBody() const; - size_t getMethodLen() const; - size_t getPathLen() const; - size_t getBodyLen() const; - std::map getQuery() const; - std::vector pathSplit() const; - private: - struct http_message* m_message; - }; // HTTPRequest - - /** - * @brief Response wrapper for an HTTP response. - */ - class HTTPResponse { - public: - HTTPResponse(struct mg_connection *nc); - void addHeader(const std::string& name, const std::string& value); - void addHeader(std::string&& name, std::string&& value); - void setStatus(int status); - void setHeaders(const std::map& headers); - void setHeaders(std::map&& headers); - void sendData(const std::string& data); - void sendData(const uint8_t* pData, size_t length); - void sendData(const char* pData, size_t length); - const std::string& getRootPath() const; - void setRootPath(const std::string& path); - void setRootPath(std::string&& path); - void sendChunkHead(); - void sendChunk(const char* pData, size_t length); - void closeConnection(); - private: - struct mg_connection *m_nc; - std::string m_rootPath; - int m_status; - std::map m_headers; - bool m_dataSent; - std::string buildHeaders(); - }; // HTTPResponse - - /** - * @brief Handler for a Multipart. - * - * This class is usually subclassed to provide your own implementation. Typically - * a factory is implemented based on HTTPMultiPartFactory that creates instances. This - * is then registered with the WebServer. When done, when ever the WebServer receives multi - * part requests, this handler for Multipart is called. The call sequence is usually: - * ~~~ - * multipartStart - * begin - * data* - * end - * begin - * data* - * end - * ... - * multipartEnd - * ~~~ - * - * There can be multiple begin, data, data, ..., end groups. - */ - class HTTPMultiPart { - public: - virtual ~HTTPMultiPart() = default; - virtual void begin(const std::string& varName, const std::string& fileName); - virtual void end(); - virtual void data(const std::string& data); - virtual void multipartEnd(); - virtual void multipartStart(); - }; // HTTPMultiPart - - /** - * @brief Factory for creating Multipart instances. - * This class is meant to be implemented to provide a constructor for a custom - * HTTPMultiPart instance. - * @code{.cpp} - * class MyMultiPart : public WebServer::HTTPMultiPart { - * public: - * void begin(std::string varName, std::string fileName) { - * ESP_LOGD(tag, "MyMultiPart begin(): varName=%s, fileName=%s", - * varName.c_str(), fileName.c_str()); - * } - * - * void end() { - * ESP_LOGD(tag, "MyMultiPart end()"); - * } - * - * void data(std::string data) { - * ESP_LOGD(tag, "MyMultiPart data(): length=%d", data.length()); - * } - * - * void multipartEnd() { - * ESP_LOGD(tag, "MyMultiPart multipartEnd()"); - * } - * - * void multipartStart() { - * ESP_LOGD(tag, "MyMultiPart multipartStart()"); - * } - * }; - * - * class MyMultiPartFactory : public WebServer::HTTPMultiPartFactory { - * WebServer::HTTPMultiPart *createNew() { - * return new MyMultiPart(); - * } - * }; - * @endcode - */ - class HTTPMultiPartFactory { - public: - /** - * @brief Create a new HTTPMultiPart instance. - * @return A new HTTPMultiPart instance. - */ - virtual HTTPMultiPart *newInstance() = 0; - }; - - /** - * @brief The handler for path matching. - * - */ - class PathHandler { - public: - PathHandler(const std::string& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - PathHandler(std::string&& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - bool match(const char* method, size_t method_len, const char* path); - void invoke(HTTPRequest *request, HTTPResponse *response); - private: - std::string m_method; - std::regex m_pattern; - void (*m_requestHandler)(WebServer::HTTPRequest *pHttpRequest, WebServer::HTTPResponse *pHttpResponse); - }; // PathHandler - - /** - * @brief A WebSocket handler for handling WebSockets. - */ - class WebSocketHandler { - public: - void onCreated(); - virtual void onMessage(const std::string& message); - void onClosed(); - void sendData(const std::string& message); - void sendData(const uint8_t* data, uint32_t size); - void close(); - private: - struct mg_connection *m_mgConnection; - }; - - class WebSocketHandlerFactory { - public: - virtual WebSocketHandler *newInstance() = 0; - }; - - WebServer(); - virtual ~WebServer(); - void addPathHandler(const std::string& method, const std::string& pathExpr, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - void addPathHandler(std::string&& method, const std::string& pathExpr, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - const std::string& getRootPath(); - void setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory); - void setRootPath(const std::string& path); - void setRootPath(std::string&& path); - void setWebSocketHandlerFactory(WebSocketHandlerFactory *pWebSocketHandlerFactory); - void start(unsigned short port = 80); - void processRequest(struct mg_connection *mgConnection, struct http_message *message); - void continueConnection(struct mg_connection* mgConnection); - HTTPMultiPartFactory *m_pMultiPartFactory; - WebSocketHandlerFactory *m_pWebSocketHandlerFactory; + /** + * @brief Request wrapper for an HTTP request. + */ + class HTTPRequest { + public: + HTTPRequest(struct http_message* message); + const char* getMethod() const; + const char* getPath() const; + const char* getBody() const; + size_t getMethodLen() const; + size_t getPathLen() const; + size_t getBodyLen() const; + std::map getQuery() const; + std::vector pathSplit() const; + + private: + struct http_message* m_message; + + }; // HTTPRequest + + /** + * @brief Response wrapper for an HTTP response. + */ + class HTTPResponse { + public: + HTTPResponse(struct mg_connection* nc); + void addHeader(const std::string& name, const std::string& value); + void addHeader(std::string&& name, std::string&& value); + void setStatus(int status); + void setHeaders(const std::map& headers); + void setHeaders(std::map&& headers); + void sendData(const std::string& data); + void sendData(const uint8_t* pData, size_t length); + void sendData(const char* pData, size_t length); + const std::string& getRootPath() const; + void setRootPath(const std::string& path); + void setRootPath(std::string&& path); + void sendChunkHead(); + void sendChunk(const char* pData, size_t length); + void closeConnection(); + + private: + struct mg_connection* m_nc; + std::string m_rootPath; + int m_status; + std::map m_headers; + bool m_dataSent; + std::string buildHeaders(); + + }; // HTTPResponse + + /** + * @brief Handler for a Multipart. + * + * This class is usually subclassed to provide your own implementation. Typically + * a factory is implemented based on HTTPMultiPartFactory that creates instances. This + * is then registered with the WebServer. When done, when ever the WebServer receives multi + * part requests, this handler for Multipart is called. The call sequence is usually: + * ~~~ + * multipartStart + * begin + * data* + * end + * begin + * data* + * end + * ... + * multipartEnd + * ~~~ + * + * There can be multiple begin, data, data, ..., end groups. + */ + class HTTPMultiPart { + public: + virtual ~HTTPMultiPart() = default; + virtual void begin(const std::string& varName, const std::string& fileName); + virtual void end(); + virtual void data(const std::string& data); + virtual void multipartEnd(); + virtual void multipartStart(); + + }; // HTTPMultiPart + + /** + * @brief Factory for creating Multipart instances. + * This class is meant to be implemented to provide a constructor for a custom + * HTTPMultiPart instance. + * @code{.cpp} + * class MyMultiPart : public WebServer::HTTPMultiPart { + * public: + * void begin(std::string varName, std::string fileName) { + * ESP_LOGD(tag, "MyMultiPart begin(): varName=%s, fileName=%s", + * varName.c_str(), fileName.c_str()); + * } + * + * void end() { + * ESP_LOGD(tag, "MyMultiPart end()"); + * } + * + * void data(std::string data) { + * ESP_LOGD(tag, "MyMultiPart data(): length=%d", data.length()); + * } + * + * void multipartEnd() { + * ESP_LOGD(tag, "MyMultiPart multipartEnd()"); + * } + * + * void multipartStart() { + * ESP_LOGD(tag, "MyMultiPart multipartStart()"); + * } + * }; + * + * class MyMultiPartFactory : public WebServer::HTTPMultiPartFactory { + * WebServer::HTTPMultiPart *createNew() { + * return new MyMultiPart(); + * } + * }; + * @endcode + */ + class HTTPMultiPartFactory { + public: + /** + * @brief Create a new HTTPMultiPart instance. + * @return A new HTTPMultiPart instance. + */ + virtual HTTPMultiPart* newInstance() = 0; + + }; + + /** + * @brief The handler for path matching. + * + */ + class PathHandler { + public: + PathHandler(const std::string& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + PathHandler(std::string&& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + bool match(const char* method, size_t method_len, const char* path); + void invoke(HTTPRequest* request, HTTPResponse* response); + + private: + std::string m_method; + std::regex m_pattern; + void (*m_requestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse); + + }; // PathHandler + + /** + * @brief A WebSocket handler for handling WebSockets. + */ + class WebSocketHandler { + public: + void onCreated(); + virtual void onMessage(const std::string& message); + void onClosed(); + void sendData(const std::string& message); + void sendData(const uint8_t* data, uint32_t size); + void close(); + + private: + struct mg_connection* m_mgConnection; + + }; + + class WebSocketHandlerFactory { + public: + virtual WebSocketHandler* newInstance() = 0; + + }; + + WebServer(); + virtual ~WebServer(); + void addPathHandler(const std::string& method, const std::string& pathExpr, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + void addPathHandler(std::string&& method, const std::string& pathExpr, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + const std::string& getRootPath(); + void setMultiPartFactory(HTTPMultiPartFactory* pMultiPartFactory); + void setRootPath(const std::string& path); + void setRootPath(std::string&& path); + void setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHandlerFactory); + void start(unsigned short port = 80); + void processRequest(struct mg_connection* mgConnection, struct http_message* message); + void continueConnection(struct mg_connection* mgConnection); + HTTPMultiPartFactory* m_pMultiPartFactory; + WebSocketHandlerFactory* m_pWebSocketHandlerFactory; + private: - std::string m_rootPath; - std::vector m_pathHandlers; - std::map unfinishedConnection; + std::string m_rootPath; + std::vector m_pathHandlers; + std::map unfinishedConnection; + }; #endif // CONFIG_MONGOOSE_PRESENT diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index e7fc05fb..aa9a7c57 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -50,8 +50,8 @@ struct Frame { */ static void dumpFrame(Frame frame) { std::ostringstream oss; - oss << "Fin: " << (int)frame.fin << ", OpCode: " << (int)frame.opCode; - switch(frame.opCode) { + oss << "Fin: " << (int) frame.fin << ", OpCode: " << (int) frame.opCode; + switch (frame.opCode) { case OPCODE_BINARY: { oss << " BINARY"; break; @@ -81,7 +81,7 @@ static void dumpFrame(Frame frame) { break; } } - oss << ", Mask: " << (int)frame.mask << ", len: " << (int)frame.len; + oss << ", Mask: " << (int) frame.mask << ", len: " << (int) frame.len; ESP_LOGD(LOG_TAG, "WebSocket frame: %s", oss.str().c_str()); } // dumpFrame @@ -101,6 +101,7 @@ class WebSocketReader: public Task { void end() { m_end = true; } + private: bool m_end; /** @@ -114,18 +115,16 @@ class WebSocketReader: public Task { Socket peerSocket = pWebSocket->getSocket(); Frame frame; - while(1) { - if (m_end) { - break; - } - ESP_LOGD("WebSocketReader", "Waiting on socket data for socket %s", peerSocket.toString().c_str()); - int length = peerSocket.receive((uint8_t*)&frame, sizeof(frame), true); // Read exact + while (true) { + if (m_end) break; + ESP_LOGD(LOG_TAG, "Waiting on socket data for socket %s", peerSocket.toString().c_str()); + int length = peerSocket.receive((uint8_t*) &frame, sizeof(frame), true); // Read exact if (length != sizeof(frame)) { ESP_LOGD(LOG_TAG, "Socket read error"); pWebSocket->close(); return; } - ESP_LOGD("WebSocketReader", "Received data from web socket. Length: %d", length); + ESP_LOGD(LOG_TAG, "Received data from web socket. Length: %d", length); dumpFrame(frame); // The following section parses the WebSocket frame. @@ -135,12 +134,12 @@ class WebSocketReader: public Task { payloadLen = frame.len; } else if (frame.len == 126) { uint16_t tempLen; - peerSocket.receive((uint8_t*)&tempLen, sizeof(tempLen), true); + peerSocket.receive((uint8_t*) &tempLen, sizeof(tempLen), true); payloadLen = ntohs(tempLen); } else if (frame.len == 127) { uint64_t tempLen; - peerSocket.receive((uint8_t*)&tempLen, sizeof(tempLen), true); - payloadLen = ntohl((uint32_t)tempLen); + peerSocket.receive((uint8_t*) &tempLen, sizeof(tempLen), true); + payloadLen = ntohl((uint32_t) tempLen); } if (frame.mask == 1) { peerSocket.receive(mask, sizeof(mask), true); @@ -152,12 +151,12 @@ class WebSocketReader: public Task { ESP_LOGD("WebSocketReader", "Web socket payload, length=%d:", payloadLen); } - WebSocketHandler *pWebSocketHandler = pWebSocket->getHandler(); - switch(frame.opCode) { + WebSocketHandler* pWebSocketHandler = pWebSocket->getHandler(); + switch (frame.opCode) { case OPCODE_TEXT: case OPCODE_BINARY: { if (pWebSocketHandler != nullptr) { - WebSocketInputStreambuf streambuf(pWebSocket->getSocket(), payloadLen, frame.mask==1?mask:nullptr); + WebSocketInputStreambuf streambuf(pWebSocket->getSocket(), payloadLen, (frame.mask == 1) ? mask : nullptr); pWebSocketHandler->onMessage(&streambuf, pWebSocket); //streambuf.discard(); } @@ -187,12 +186,12 @@ class WebSocketReader: public Task { } default: { - ESP_LOGD("WebSocketReader", "Unknown opcode: %d", frame.opCode); + ESP_LOGD("WebSocketReader", "Unknown opcode: %d", frame.opCode); break; } } // Switch opCode - } // While (1) + } // while (true) ESP_LOGD("WebSocketReader", "<< run"); } // run }; // WebSocketReader @@ -218,7 +217,7 @@ void WebSocketHandler::onClose() { * ``` * This will read the whole message into the string stream. */ -void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf, WebSocket *pWebSocket) { +void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf, WebSocket* pWebSocket) { ESP_LOGD("WebSocketHandler", ">> onMessage"); ESP_LOGD("WebSocketHandler", "<< onMessage"); } // onData @@ -279,7 +278,7 @@ void WebSocket::close(uint16_t status, std::string message) { frame.opCode = OPCODE_CLOSE; frame.mask = 0; frame.len = message.length() + 2; - int rc = m_socket.send((uint8_t *)&frame, sizeof(frame)); + int rc = m_socket.send((uint8_t*) &frame, sizeof(frame)); if (rc > 0) { rc = m_socket.send(status); @@ -329,15 +328,15 @@ void WebSocket::send(std::string data, uint8_t sendType) { frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; - frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; + frame.opCode = (sendType == SEND_TYPE_TEXT) ? OPCODE_TEXT : OPCODE_BINARY; frame.mask = 0; if (data.length() < 126) { frame.len = data.length(); - m_socket.send((uint8_t *)&frame, sizeof(frame)); + m_socket.send((uint8_t*) &frame, sizeof(frame)); } else { frame.len = 126; - m_socket.send((uint8_t *)&frame, sizeof(frame)); - m_socket.send(htons((uint16_t)data.length())); // Convert to network byte order from host byte order + m_socket.send((uint8_t*) &frame, sizeof(frame)); + m_socket.send(htons((uint16_t) data.length())); // Convert to network byte order from host byte order } m_socket.send((uint8_t*)data.data(), data.length()); ESP_LOGD(LOG_TAG, "<< send"); @@ -358,14 +357,14 @@ void WebSocket::send(uint8_t* data, uint16_t length, uint8_t sendType) { frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; - frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; + frame.opCode = (sendType==SEND_TYPE_TEXT) ? OPCODE_TEXT : OPCODE_BINARY; frame.mask = 0; if (length < 126) { frame.len = length; - m_socket.send((uint8_t *)&frame, sizeof(frame)); + m_socket.send((uint8_t*) &frame, sizeof(frame)); } else { frame.len = 126; - m_socket.send((uint8_t *)&frame, sizeof(frame)); + m_socket.send((uint8_t*) &frame, sizeof(frame)); m_socket.send(htons(length)); // Convert to network byte order from host byte order } m_socket.send(data, length); @@ -405,7 +404,7 @@ void WebSocket::startReader() { WebSocketInputStreambuf::WebSocketInputStreambuf( Socket socket, size_t dataLength, - uint8_t *pMask, + uint8_t* pMask, size_t bufferSize) { m_socket = socket; // The socket we will be reading from m_dataLength = dataLength; // The size of the record we wish to read. @@ -476,7 +475,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { // We wish to refill the buffer. We want to read data from the socket. We want to read either // the size of the buffer to fill it or the maximum number of bytes remaining to be read. // We will choose which ever is smaller as the number of bytes to read into the buffer. - int remainingBytes = getRecordSize()-m_sizeRead; + int remainingBytes = getRecordSize() - m_sizeRead; size_t sizeToRead; if (remainingBytes < m_bufferSize) { sizeToRead = remainingBytes; @@ -485,7 +484,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { } ESP_LOGD("WebSocketInputRecordStreambuf", "- getting next buffer of data; size request: %d", sizeToRead); - int bytesRead = m_socket.receive((uint8_t*)m_buffer, sizeToRead, true); + int bytesRead = m_socket.receive((uint8_t*) m_buffer, sizeToRead, true); if (bytesRead == 0) { ESP_LOGD("WebSocketInputRecordStreambuf", "<< underflow: Read 0 bytes"); return EOF; @@ -493,8 +492,8 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { // If the WebSocket frame shows that we have a mask bit set then we have to unmask the data. if (m_pMask != nullptr) { - for (int i=0; i> onMessage"); // Test to see if we are currently active. If not, this is the start of a transfer. if (!m_active) { - ESP_LOGD("FileTransferWebSocketHandler", "Not yet active!"); // Read a chunk of data into memory. std::stringstream buffer; @@ -82,7 +71,7 @@ class FileTransferWebSocketHandler : public WebSocketHandler { // "length": // Length of file. Optional. // } JsonObject jo = JSON::parseObject(buffer.str()); - m_fileName = jo.getString("name"); + m_fileName = jo.getString("name"); assert(m_fileName.length() > 0); // Doesn't make any sense to receive a zero length file name. if (jo.hasItem("length")) { m_fileLength = jo.getInt("length"); @@ -91,9 +80,9 @@ class FileTransferWebSocketHandler : public WebSocketHandler { ESP_LOGD("FileTransferWebSocketHandler", "Target file is %s", fileName.c_str()); // If the file to create ends in a "/" then we are being asked to create a directory. - if (m_fileName.substr(m_fileName.size()-1)=="/") { + if (m_fileName.substr(m_fileName.size() - 1) == "/") { ESP_LOGD("FileTransferWebSocketHandler", "Is a directory!!"); - fileName = fileName.substr(0, fileName.size()-1); // Remove the trailing slash + fileName = fileName.substr(0, fileName.size() - 1); // Remove the trailing slash struct stat statbuf; if (stat(fileName.c_str(), &statbuf) == 0) { if (S_ISREG(statbuf.st_mode)) { @@ -114,7 +103,7 @@ class FileTransferWebSocketHandler : public WebSocketHandler { return; } } - m_active = true; + m_active = true; ESP_LOGD("FileTransferWebSocketHandler", "Filename: %s, length: %d", fileName.c_str(), m_fileLength); } // !active --- Not active else { @@ -133,7 +122,6 @@ class FileTransferWebSocketHandler : public WebSocketHandler { } } // onMessage - /** * @brief Handle a close event on the web socket. */ @@ -146,8 +134,17 @@ class FileTransferWebSocketHandler : public WebSocketHandler { if (m_ofStream.is_open()) { m_ofStream.close(); // Close the file now that we have finished writing to it. } - delete this; // Delete ourself. + delete this; // Delete ourselves. } // onClose + +private: + std::string m_fileName; // The name of the file we are receiving. + uint32_t m_fileLength; // We may optionally receive a file length. + uint32_t m_sizeReceived; // The size of the data actually received so far. + bool m_active; // Are we actively processing a file. + std::ofstream m_ofStream; // The file stream we are writing to when active. + std::string m_rootPath; // The root path for file names. + }; // FileTransferWebSocketHandler } // End un-named namespace diff --git a/cpp_utils/WebSocketFileTransfer.h b/cpp_utils/WebSocketFileTransfer.h index 98cbda9b..6eb8de43 100644 --- a/cpp_utils/WebSocketFileTransfer.h +++ b/cpp_utils/WebSocketFileTransfer.h @@ -17,7 +17,7 @@ class WebSocketFileTransfer { public: WebSocketFileTransfer(std::string rootPath); - void start(WebSocket *pWebSocket); + void start(WebSocket* pWebSocket); }; #endif /* COMPONENTS_CPP_UTILS_WEBSOCKETFILETRANSFER_H_ */ diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 09d59d55..3c6d4112 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -47,10 +47,10 @@ static void setDNSServer(char *ip) { * @brief Creates and uses a default event handler */ WiFi::WiFi() - : ip(0) - , gw(0) - , netmask(0) - , m_pWifiEventHandler(nullptr) + : ip(0) + , gw(0) + , netmask(0) + , m_pWifiEventHandler(nullptr) { m_eventLoopStarted = false; m_initCalled = false; @@ -94,14 +94,14 @@ void WiFi::addDNSServer(const std::string& ip) { void WiFi::addDNSServer(const char* ip) { ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { + if (inet_pton(AF_INET, ip, &dns_server)) { addDNSServer(ip); } } // addDNSServer void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*) (&ip))[0], ((uint8_t*) (&ip))[1], ((uint8_t*) (&ip))[2], ((uint8_t*) (&ip))[3]); init(); ::dns_setserver(m_dnsCount, &ip); m_dnsCount++; @@ -132,14 +132,14 @@ void WiFi::setDNSServer(int numdns, const std::string& ip) { void WiFi::setDNSServer(int numdns, const char* ip) { ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { + if (inet_pton(AF_INET, ip, &dns_server)) { setDNSServer(numdns, dns_server); } } // setDNSServer void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*) (&ip))[0], ((uint8_t*) (&ip))[1], ((uint8_t*) (&ip))[2], ((uint8_t*) (&ip))[3]); init(); ::dns_setserver(numdns, &ip); } // setDNSServer @@ -163,14 +163,14 @@ uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bo init(); if (ip != 0 && gw != 0 && netmask != 0) { - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); } esp_err_t errRc = ::esp_wifi_set_mode(mode); @@ -195,23 +195,22 @@ uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bo } m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. - do { - ESP_LOGD(LOG_TAG, "esp_wifi_connect"); - errRc = ::esp_wifi_connect(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } - } - while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s - m_connectFinished.give(); + do { + ESP_LOGD(LOG_TAG, "esp_wifi_connect"); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } + } + while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s + m_connectFinished.give(); ESP_LOGD(LOG_TAG, "<< connectAP"); return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. } // connectAP - /** * @brief Dump diagnostics to the log. */ @@ -233,7 +232,6 @@ bool WiFi::isConnectedToAP() { } // isConnected - /** * @brief Primary event handler interface. */ @@ -243,7 +241,7 @@ bool WiFi::isConnectedToAP() { // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this // an indirection vector to the real caller. - WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. + WiFi* pWiFi = (WiFi*) ctx; // retrieve the WiFi object from the passed in context. // Invoke the event handler. esp_err_t rc; @@ -256,7 +254,6 @@ bool WiFi::isConnectedToAP() { // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that // indicates we are waiting for a connection complete. if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. pWiFi->m_apConnectionStatus = ESP_OK; } else { @@ -303,14 +300,15 @@ std::string WiFi::getApSSID() { wifi_config_t conf; //init(); esp_wifi_get_config(WIFI_IF_AP, &conf); - return std::string((char *)conf.sta.ssid); + return std::string((char*) conf.sta.ssid); } // getApSSID + /** * @brief Get the current ESP32 IP form AP. * @return The ESP32 IP. */ -std::string WiFi::getApIp(){ +std::string WiFi::getApIp() { tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); @@ -321,7 +319,7 @@ std::string WiFi::getApIp(){ * @brief Get the current AP netmask. * @return The Netmask IP. */ -std::string WiFi::getApNetmask(){ +std::string WiFi::getApNetmask() { tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); @@ -332,7 +330,7 @@ std::string WiFi::getApNetmask(){ * @brief Get the current AP Gateway IP. * @return The Gateway IP. */ -std::string WiFi::getApGateway(){ +std::string WiFi::getApGateway() { tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); @@ -353,13 +351,13 @@ struct in_addr WiFi::getHostByName(const std::string& hostName) { struct in_addr WiFi::getHostByName(const char* hostName) { struct in_addr retAddr; - struct hostent *he = gethostbyname(hostName); + struct hostent* he = gethostbyname(hostName); if (he == nullptr) { retAddr.s_addr = 0; ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); } else { - retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + retAddr = *(struct in_addr*) (he->h_addr_list[0]); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t*) &retAddr); } return retAddr; } // getHostByName @@ -372,7 +370,7 @@ struct in_addr WiFi::getHostByName(const char* hostName) { std::string WiFi::getMode() { wifi_mode_t mode; esp_wifi_get_mode(&mode); - switch(mode) { + switch (mode) { case WIFI_MODE_NULL: return "WIFI_MODE_NULL"; case WIFI_MODE_STA: @@ -397,11 +395,12 @@ tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { return ipInfo; } // getStaIpInfo + /** * @brief Get the current ESP32 IP form STA. * @return The ESP32 IP. */ -std::string WiFi::getStaIp(){ +std::string WiFi::getStaIp() { tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); @@ -413,7 +412,7 @@ std::string WiFi::getStaIp(){ * @brief Get the current STA netmask. * @return The Netmask IP. */ -std::string WiFi::getStaNetmask(){ +std::string WiFi::getStaNetmask() { tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); @@ -425,13 +424,14 @@ std::string WiFi::getStaNetmask(){ * @brief Get the current STA Gateway IP. * @return The Gateway IP. */ -std::string WiFi::getStaGateway(){ +std::string WiFi::getStaGateway() { tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); char ipAddrStr[30]; inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); return std::string(ipAddrStr); } // getStaGateway + /** * @brief Get the MAC address of the STA interface. * @return The MAC address of the STA interface. @@ -452,7 +452,7 @@ std::string WiFi::getStaMac() { std::string WiFi::getStaSSID() { wifi_config_t conf; esp_wifi_get_config(WIFI_IF_STA, &conf); - return std::string((char *)conf.ap.ssid); + return std::string((char*) conf.ap.ssid); } // getStaSSID @@ -460,7 +460,6 @@ std::string WiFi::getStaSSID() { * @brief Initialize WiFi. */ /* PRIVATE */ void WiFi::init() { - // If we have already started the event loop, then change the handler otherwise // start the event loop. if (m_eventLoopStarted) { @@ -530,15 +529,20 @@ std::vector WiFi::scan() { esp_err_t rc = ::esp_wifi_scan_start(&conf, true); if (rc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); - return apRecords; + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); + return apRecords; } uint16_t apCount; // Number of access points available. rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_num: %d", rc); + return apRecords; + } else { + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + } - wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + wifi_ap_record_t* list = (wifi_ap_record_t*) malloc(sizeof(wifi_ap_record_t) * apCount); if (list == nullptr) { ESP_LOGE(LOG_TAG, "Failed to allocate memory"); return apRecords; @@ -550,18 +554,18 @@ std::vector WiFi::scan() { abort(); } - for (auto i=0; i rhs.m_rssi;}); + [](const WiFiAPRecord& lhs, const WiFiAPRecord& rhs){ return lhs.m_rssi > rhs.m_rssi; }); return apRecords; } // scan @@ -584,6 +588,7 @@ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_au startAP(ssid, password, auth, 0, false, 4); } // startAP + /** * @brief Start being an access point. * @@ -649,9 +654,9 @@ void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_au * @param[in] wifiEventHandler The class that will be used to process events. */ void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { - ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); - this->m_pWifiEventHandler = wifiEventHandler; - ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); + ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t) wifiEventHandler); + this->m_pWifiEventHandler = wifiEventHandler; + ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); } // setWifiEventHandler @@ -677,19 +682,16 @@ void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::st } // setIPInfo - void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { uint32_t new_ip; uint32_t new_gw; uint32_t new_netmask; - auto success = (bool)inet_pton(AF_INET, ip, &new_ip); + auto success = (bool) inet_pton(AF_INET, ip, &new_ip); success = success && inet_pton(AF_INET, gw, &new_gw); success = success && inet_pton(AF_INET, netmask, &new_netmask); - if(!success) { - return; - } + if (!success) return; setIPInfo(new_ip, new_gw, new_netmask); } // setIPInfo @@ -708,7 +710,7 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { this->gw = gw; this->netmask = netmask; - if(ip != 0 && gw != 0 && netmask != 0) { + if (ip != 0 && gw != 0 && netmask != 0) { tcpip_adapter_ip_info_t ipInfo; ipInfo.ip.addr = ip; ipInfo.gw.addr = gw; @@ -729,7 +731,7 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { */ std::string WiFiAPRecord::toString() { std::string auth; - switch(getAuthMode()) { + switch (getAuthMode()) { case WIFI_AUTH_OPEN: auth = "WIFI_AUTH_OPEN"; break; @@ -745,17 +747,18 @@ std::string WiFiAPRecord::toString() { case WIFI_AUTH_WPA_WPA2_PSK: auth = "WIFI_AUTH_WPA_WPA2_PSK"; break; - default: - auth = ""; - break; - } -// std::stringstream s; -// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; - auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); - sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); - return std::string(std::move(info_str)); + default: + auth = ""; + break; + } +// std::stringstream s; +// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; + auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); + sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); + return std::string(std::move(info_str)); } // toString + /* MDNS::MDNS() { esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); @@ -765,6 +768,7 @@ MDNS::MDNS() { } } + MDNS::~MDNS() { if (m_mdns_server != nullptr) { mdns_free(m_mdns_server); @@ -773,6 +777,7 @@ MDNS::~MDNS() { } */ + /** * @brief Define the service for mDNS. * @@ -783,25 +788,26 @@ MDNS::~MDNS() { */ /* void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { - serviceAdd(service.c_str(), proto.c_str(), port); + serviceAdd(service.c_str(), proto.c_str(), port); } // serviceAdd void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { - serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); + serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); } // serviceInstanceSet void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { - servicePortSet(service.c_str(), proto.c_str(), port); + servicePortSet(service.c_str(), proto.c_str(), port); } // servicePortSet void MDNS::serviceRemove(const std::string& service, const std::string& proto) { - serviceRemove(service.c_str(), proto.c_str()); + serviceRemove(service.c_str(), proto.c_str()); } // serviceRemove */ + /** * @brief Set the mDNS hostname. * @@ -810,10 +816,11 @@ void MDNS::serviceRemove(const std::string& service, const std::string& proto) { */ /* void MDNS::setHostname(const std::string& hostname) { - setHostname(hostname.c_str()); + setHostname(hostname.c_str()); } // setHostname */ + /** * @brief Set the mDNS instance. * @@ -822,10 +829,11 @@ void MDNS::setHostname(const std::string& hostname) { */ /* void MDNS::setInstance(const std::string& instance) { - setInstance(instance.c_str()); + setInstance(instance.c_str()); } // setInstance */ + /** * @brief Define the service for mDNS. * @@ -863,14 +871,15 @@ void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) void MDNS::serviceRemove(const char* service, const char* proto) { - esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // serviceRemove - */ + + /** * @brief Set the mDNS hostname. * @@ -880,13 +889,14 @@ void MDNS::serviceRemove(const char* service, const char* proto) { /* void MDNS::setHostname(const char* hostname) { esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setHostname */ + /** * @brief Set the mDNS instance. * @@ -895,10 +905,10 @@ void MDNS::setHostname(const char* hostname) { */ /* void MDNS::setInstance(const char* instance) { - esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setInstance */ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index c1b6bedc..c0bfa07a 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -22,62 +22,65 @@ /* class MDNS { public: - MDNS(); - ~MDNS(); - void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); - void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); - void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); - void serviceRemove(const std::string& service, const std::string& proto); - void setHostname(const std::string& hostname); - void setInstance(const std::string& instance); - // If we the above functions with a basic char*, a copy would be created into an std::string, - // making the whole thing require twice as much processing power and speed - void serviceAdd(const char* service, const char* proto, uint16_t port); - void serviceInstanceSet(const char* service, const char* proto, const char* instance); - void servicePortSet(const char* service, const char* proto, uint16_t port); - void serviceRemove(const char* service, const char* proto); - void setHostname(const char* hostname); - void setInstance(const char* instance); + MDNS(); + ~MDNS(); + void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); + void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); + void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); + void serviceRemove(const std::string& service, const std::string& proto); + void setHostname(const std::string& hostname); + void setInstance(const std::string& instance); + // If we the above functions with a basic char*, a copy would be created into an std::string, + // making the whole thing require twice as much processing power and speed + void serviceAdd(const char* service, const char* proto, uint16_t port); + void serviceInstanceSet(const char* service, const char* proto, const char* instance); + void servicePortSet(const char* service, const char* proto, uint16_t port); + void serviceRemove(const char* service, const char* proto); + void setHostname(const char* hostname); + void setInstance(const char* instance); + private: - mdns_server_t *m_mdns_server = nullptr; + mdns_server_t* m_mdns_server = nullptr; + }; */ class WiFiAPRecord { public: - friend class WiFi; - - /** - * @brief Get the auth mode. - * @return The auth mode. - */ - wifi_auth_mode_t getAuthMode() { - return m_authMode; - } - - /** - * @brief Get the RSSI. - * @return the RSSI. - */ - int8_t getRSSI() { - return m_rssi; - } - - /** - * @brief Get the SSID. - * @return the SSID. - */ - std::string getSSID() { - return m_ssid; - } - - std::string toString(); + friend class WiFi; + + /** + * @brief Get the auth mode. + * @return The auth mode. + */ + wifi_auth_mode_t getAuthMode() { + return m_authMode; + } + + /** + * @brief Get the RSSI. + * @return the RSSI. + */ + int8_t getRSSI() { + return m_rssi; + } + + /** + * @brief Get the SSID. + * @return the SSID. + */ + std::string getSSID() { + return m_ssid; + } + + std::string toString(); private: - uint8_t m_bssid[6]; - int8_t m_rssi; - std::string m_ssid; - wifi_auth_mode_t m_authMode; + uint8_t m_bssid[6]; + int8_t m_rssi; + std::string m_ssid; + wifi_auth_mode_t m_authMode; + }; /** @@ -107,52 +110,53 @@ class WiFiAPRecord { */ class WiFi { private: - static esp_err_t eventHandler(void* ctx, system_event_t* event); - void init(); - uint32_t ip; - uint32_t gw; - uint32_t netmask; - WiFiEventHandler* m_pWifiEventHandler; - uint8_t m_dnsCount=0; - bool m_eventLoopStarted; - bool m_initCalled; - uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. + static esp_err_t eventHandler(void* ctx, system_event_t* event); + void init(); + uint32_t ip; + uint32_t gw; + uint32_t netmask; + WiFiEventHandler* m_pWifiEventHandler; + uint8_t m_dnsCount = 0; + bool m_eventLoopStarted; + bool m_initCalled; + uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); public: - WiFi(); - ~WiFi(); - void addDNSServer(const std::string& ip); - void addDNSServer(const char* ip); - void addDNSServer(ip_addr_t ip); - void setDNSServer(int numdns, const std::string& ip); - void setDNSServer(int numdns, const char* ip); - void setDNSServer(int numdns, ip_addr_t ip); - struct in_addr getHostByName(const std::string& hostName); - struct in_addr getHostByName(const char* hostName); - uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true, wifi_mode_t mode=WIFI_MODE_STA); - void dump(); - bool isConnectedToAP(); - static std::string getApMac(); - static tcpip_adapter_ip_info_t getApIpInfo(); - static std::string getApSSID(); + WiFi(); + ~WiFi(); + void addDNSServer(const std::string& ip); + void addDNSServer(const char* ip); + void addDNSServer(ip_addr_t ip); + void setDNSServer(int numdns, const std::string& ip); + void setDNSServer(int numdns, const char* ip); + void setDNSServer(int numdns, ip_addr_t ip); + struct in_addr getHostByName(const std::string& hostName); + struct in_addr getHostByName(const char* hostName); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection = true, wifi_mode_t mode = WIFI_MODE_STA); + void dump(); + bool isConnectedToAP(); + static std::string getApMac(); + static tcpip_adapter_ip_info_t getApIpInfo(); + static std::string getApSSID(); static std::string getApIp(); static std::string getApNetmask(); static std::string getApGateway(); - static std::string getMode(); - static tcpip_adapter_ip_info_t getStaIpInfo(); - static std::string getStaMac(); - static std::string getStaSSID(); + static std::string getMode(); + static tcpip_adapter_ip_info_t getStaIpInfo(); + static std::string getStaMac(); + static std::string getStaSSID(); static std::string getStaIp(); static std::string getStaNetmask(); static std::string getStaGateway(); - std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); - void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); - void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); - void setIPInfo(const char* ip, const char* gw, const char* netmask); - void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); + std::vector scan(); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); + void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); + void setIPInfo(const char* ip, const char* gw, const char* netmask); + void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); + void setWifiEventHandler(WiFiEventHandler* wifiEventHandler); + }; #endif /* MAIN_WIFI_H_ */ diff --git a/cpp_utils/WiFiEventHandler.cpp b/cpp_utils/WiFiEventHandler.cpp index c5a4fa2f..204a0661 100644 --- a/cpp_utils/WiFiEventHandler.cpp +++ b/cpp_utils/WiFiEventHandler.cpp @@ -24,15 +24,15 @@ static const char* LOG_TAG = "WiFiEventHandler"; * @return ESP_OK if the event was handled otherwise an error. */ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { - ESP_LOGD(LOG_TAG, ">> eventHandler called: ctx=0x%x, event=0x%x", (uint32_t)ctx, (uint32_t)event); - WiFiEventHandler *pWiFiEventHandler = (WiFiEventHandler *)ctx; + ESP_LOGD(LOG_TAG, ">> eventHandler called: ctx=0x%x, event=0x%x", (uint32_t) ctx, (uint32_t) event); + WiFiEventHandler* pWiFiEventHandler = (WiFiEventHandler*) ctx; if (ctx == nullptr) { ESP_LOGD(LOG_TAG, "No context"); return ESP_OK; } esp_err_t rc = ESP_OK; - switch(event->event_id) { + switch (event->event_id) { case SYSTEM_EVENT_AP_START: { rc = pWiFiEventHandler->apStart(); break; @@ -95,15 +95,15 @@ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { default: break; - } - - if (pWiFiEventHandler->m_nextHandler != nullptr) { - printf("Found a next handler\n"); - rc = eventHandler(pWiFiEventHandler->m_nextHandler, event); - } else { - //printf("NOT Found a next handler\n"); - } - return rc; + } + + if (pWiFiEventHandler->m_nextHandler != nullptr) { + ESP_LOGD(LOG_TAG, "Found a next handler"); + rc = eventHandler(pWiFiEventHandler->m_nextHandler, event); + } else { + //ESP_LOGD(LOG_TAG, "NOT Found a next handler"); + } + return rc; } // eventHandler @@ -134,8 +134,8 @@ system_event_cb_t WiFiEventHandler::getEventHandler() { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { - ESP_LOGD(LOG_TAG, "default staGotIp"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staGotIp"); + return ESP_OK; } // staGotIp @@ -145,8 +145,8 @@ esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStart() { - ESP_LOGD(LOG_TAG, "default apStart"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStart"); + return ESP_OK; } // apStart @@ -156,26 +156,26 @@ esp_err_t WiFiEventHandler::apStart() { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStop() { - ESP_LOGD(LOG_TAG, "default apStop"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStop"); + return ESP_OK; } // apStop esp_err_t WiFiEventHandler::wifiReady() { - ESP_LOGD(LOG_TAG, "default wifiReady"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default wifiReady"); + return ESP_OK; } // wifiReady esp_err_t WiFiEventHandler::staStart() { - ESP_LOGD(LOG_TAG, "default staStart"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staStart"); + return ESP_OK; } // staStart esp_err_t WiFiEventHandler::staStop() { - ESP_LOGD(LOG_TAG, "default staStop"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staStop"); + return ESP_OK; } // staStop @@ -186,8 +186,8 @@ esp_err_t WiFiEventHandler::staStop() { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staConnected(system_event_sta_connected_t info) { - ESP_LOGD(LOG_TAG, "default staConnected"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staConnected"); + return ESP_OK; } // staConnected @@ -198,8 +198,8 @@ esp_err_t WiFiEventHandler::staConnected(system_event_sta_connected_t info) { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { - ESP_LOGD(LOG_TAG, "default staDisconnected"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staDisconnected"); + return ESP_OK; } // staDisconnected @@ -210,8 +210,8 @@ esp_err_t WiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStaConnected(system_event_ap_staconnected_t info) { - ESP_LOGD(LOG_TAG, "default apStaConnected"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStaConnected"); + return ESP_OK; } // apStaConnected @@ -222,8 +222,8 @@ esp_err_t WiFiEventHandler::apStaConnected(system_event_ap_staconnected_t info) * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStaDisconnected(system_event_ap_stadisconnected_t info) { - ESP_LOGD(LOG_TAG, "default apStaDisconnected"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStaDisconnected"); + return ESP_OK; } // apStaDisconnected @@ -234,8 +234,8 @@ esp_err_t WiFiEventHandler::apStaDisconnected(system_event_ap_stadisconnected_t * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staScanDone(system_event_sta_scan_done_t info) { - ESP_LOGD(LOG_TAG, "default staScanDone"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staScanDone"); + return ESP_OK; } // staScanDone @@ -246,8 +246,8 @@ esp_err_t WiFiEventHandler::staScanDone(system_event_sta_scan_done_t info) { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::staAuthChange(system_event_sta_authmode_change_t info) { - ESP_LOGD(LOG_TAG, "default staAuthChange"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staAuthChange"); + return ESP_OK; } // staAuthChange diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index 4dd46dba..9efb837f 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -117,9 +117,10 @@ class WiFiEventHandler { * Get the next WiFi event handler in the chain, if there is one. * @return The next WiFi event handler in the chain or nullptr if there is none. */ - WiFiEventHandler *getNextHandler() { + WiFiEventHandler* getNextHandler() { return m_nextHandler; } + /** * Set the next WiFi event handler in the chain. * @param [in] nextHandler The next WiFi event handler in the chain. @@ -132,6 +133,7 @@ class WiFiEventHandler { friend class WiFi; WiFiEventHandler *m_nextHandler; static esp_err_t eventHandler(void* ctx, system_event_t* event); + }; #endif /* MAIN_WIFIEVENTHANDLER_H_ */ diff --git a/cpp_utils/mainpage.dox b/cpp_utils/mainpage.dox index 3597471d..5701909e 100644 --- a/cpp_utils/mainpage.dox +++ b/cpp_utils/mainpage.dox @@ -5,4 +5,4 @@ * functions. * * The Github repository is [https://github.com/nkolban/esp32-snippets](https://github.com/nkolban/esp32-snippets). - */ \ No newline at end of file + */ From 5501cf62f196fc90f3e4742600fe7073d4cae8e7 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:28:03 -0600 Subject: [PATCH 257/310] Sanitize Neopixel logging --- cpp_utils/NeoPixelWiFiEventHandler.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cpp_utils/NeoPixelWiFiEventHandler.cpp b/cpp_utils/NeoPixelWiFiEventHandler.cpp index 1632a0f7..229fd7ff 100644 --- a/cpp_utils/NeoPixelWiFiEventHandler.cpp +++ b/cpp_utils/NeoPixelWiFiEventHandler.cpp @@ -5,8 +5,11 @@ * Author: kolban */ #include +#include #include "NeoPixelWiFiEventHandler.h" +static const char* LOG_TAG = "NeoPixelWiFiEventHandler"; + NeoPixelWiFiEventHandler::NeoPixelWiFiEventHandler(gpio_num_t gpioPin) { this->gpioPin = gpioPin; ws2812 = new WS2812(gpioPin, 8); @@ -17,42 +20,42 @@ NeoPixelWiFiEventHandler::~NeoPixelWiFiEventHandler() { } esp_err_t NeoPixelWiFiEventHandler::apStart() { - printf("XXX apStart\n"); + ESP_LOGD(LOG_TAG, "XXX apStart"); ws2812->setPixel(0, 0, 00, 64); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::staConnected(system_event_sta_connected_t info) { - printf("XXX staConnected\n"); + ESP_LOGD(LOG_TAG, "XXX staConnected"); ws2812->setPixel(0, 57, 89, 66); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { - printf("XXX staDisconnected\n"); + ESP_LOGD(LOG_TAG, "XXX staDisconnected"); ws2812->setPixel(0, 64, 0, 0); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::staStart() { - printf("XXX staStart\n"); + ESP_LOGD(LOG_TAG, "XXX staStart"); ws2812->setPixel(0, 64, 64, 0); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { - printf("XXX staGotIp\n"); + ESP_LOGD(LOG_TAG, "XXX staGotIp"); ws2812->setPixel(0, 0, 64, 0); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::wifiReady() { - printf("XXX wifiReady\n"); + ESP_LOGD(LOG_TAG, "XXX wifiReady"); ws2812->setPixel(0, 64, 64, 0); ws2812->show(); return ESP_OK; From 466332c6a6e7346e8ac9127a7e6b459a193b6a8f Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:32:08 -0600 Subject: [PATCH 258/310] BT: uint16_t numHandles --- cpp_utils/BLEService.cpp | 6 +++--- cpp_utils/BLEService.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 340ea560..1f293aad 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -35,7 +35,7 @@ static const char* LOG_TAG = "BLEService"; // Tag for logging. * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { +BLEService::BLEService(const char* uuid, uint16_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { } @@ -44,7 +44,7 @@ BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUU * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { +BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) { m_uuid = uuid; m_handle = NULL_HANDLE; m_pServer = nullptr; @@ -264,7 +264,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { return createCharacteristic(BLEUUID(uuid), properties); } - + /** * @brief Create a new BLE Characteristic associated with this service. diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 93b4b2c6..de4cc992 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -27,7 +27,7 @@ class BLECharacteristicMap { void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(const char* uuid); BLECharacteristic* getByUUID(BLEUUID uuid); BLECharacteristic* getByHandle(uint16_t handle); BLECharacteristic* getFirst(); @@ -69,8 +69,8 @@ class BLEService { uint8_t m_id = 0; private: - BLEService(const char* uuid, uint32_t numHandles); - BLEService(BLEUUID uuid, uint32_t numHandles); + BLEService(const char* uuid, uint16_t numHandles); + BLEService(BLEUUID uuid, uint16_t numHandles); friend class BLEServer; friend class BLEServiceMap; friend class BLEDescriptor; @@ -88,7 +88,7 @@ class BLEService { FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); - uint32_t m_numHandles; + uint16_t m_numHandles; BLECharacteristic* getLastCreatedCharacteristic(); void handleGATTServerEvent( From 814c3244a83181a50b9ebb1dc1907130f321fd6c Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:42:45 -0600 Subject: [PATCH 259/310] FreeRTOS: Check if value is pdTRUE --- cpp_utils/FreeRTOS.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 1ae01d73..77c6921e 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -134,7 +134,7 @@ void FreeRTOS::Semaphore::give() { xSemaphoreGive(m_semaphore); } // #ifdef ARDUINO_ARCH_ESP32 -// FreeRTOS::sleep(10); +// FreeRTOS::sleep(10); // #endif m_owner = std::string(""); @@ -178,7 +178,7 @@ bool FreeRTOS::Semaphore::take(std::string owner) if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); } else { - rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY); + rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; } m_owner = owner; if (rc) { @@ -204,7 +204,7 @@ bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { if (m_usePthreads) { assert(false); // We apparently don't have a timed wait for pthreads. } else { - rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; } m_owner = owner; if (rc) { From a3b7fe373c1dea042473bed295e4a88fc8443230 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:49:06 -0600 Subject: [PATCH 260/310] Return bool from Ringbuffer::send --- cpp_utils/FreeRTOS.cpp | 4 ++-- cpp_utils/FreeRTOS.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 1ae01d73..2d65bbcf 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -279,8 +279,8 @@ void Ringbuffer::returnItem(void* item) { * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. * @return */ -uint32_t Ringbuffer::send(void* data, size_t length, TickType_t wait) { - return ::xRingbufferSend(m_handle, data, length, wait); +bool Ringbuffer::send(void* data, size_t length, TickType_t wait) { + return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE; } // send diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index ab0e83d8..45013419 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -62,7 +62,7 @@ class Ringbuffer { void* receive(size_t* size, TickType_t wait = portMAX_DELAY); void returnItem(void* item); - uint32_t send(void* data, size_t length, TickType_t wait = portMAX_DELAY); + bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY); private: RingbufHandle_t m_handle; }; From 409a178be94b254e2c35d6cc5746b96e024f44b5 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 16:50:50 -0600 Subject: [PATCH 261/310] FreeRTOS: Stack size is uint32_t --- cpp_utils/FreeRTOS.cpp | 2 +- cpp_utils/FreeRTOS.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 1ae01d73..c9919ea0 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -32,7 +32,7 @@ void FreeRTOS::sleep(uint32_t ms) { * @param[in] param An optional parameter to be passed to the started task. * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. */ -void FreeRTOS::startTask(void task(void*), std::string taskName, void *param, int stackSize) { +void FreeRTOS::startTask(void task(void*), std::string taskName, void *param, uint32_t stackSize) { ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); } // startTask diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index ab0e83d8..e13062be 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -23,7 +23,7 @@ class FreeRTOS { public: static void sleep(uint32_t ms); - static void startTask(void task(void *), std::string taskName, void *param=nullptr, int stackSize = 2048); + static void startTask(void task(void *), std::string taskName, void *param=nullptr, uint32_t stackSize = 2048); static void deleteTask(TaskHandle_t pTask = nullptr); static uint32_t getTimeSinceStart(); From 009d00336527459ed31f408cba983072fa7c02db Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:11:52 -0600 Subject: [PATCH 262/310] GPIO: Add checks for bool returns --- cpp_utils/GPIO.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/GPIO.cpp b/cpp_utils/GPIO.cpp index 23cf6fb7..38bd19a4 100644 --- a/cpp_utils/GPIO.cpp +++ b/cpp_utils/GPIO.cpp @@ -124,7 +124,7 @@ void ESP32CPP::GPIO::low(gpio_num_t pin) { * @return True if the pin is high, false if the pin is low. */ bool ESP32CPP::GPIO::read(gpio_num_t pin) { - return ::gpio_get_level(pin); + return ::gpio_get_level(pin) == 1; } // read @@ -189,7 +189,7 @@ void ESP32CPP::GPIO::setOutput(gpio_num_t pin) { */ void ESP32CPP::GPIO::write(gpio_num_t pin, bool value) { //ESP_LOGD(LOG_TAG, ">> write: pin: %d, value: %d", pin, value); - esp_err_t errRc = ::gpio_set_level(pin, value); + esp_err_t errRc = ::gpio_set_level(pin, value ? 1 : 0); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< gpio_set_level: pin=%d, rc=%d %s", pin, errRc, GeneralUtils::errorToString(errRc)); } From 18e77c7abeef0536d6d728763e6c27fa49ae8369 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:13:27 -0600 Subject: [PATCH 263/310] I2C: Configurable pullups --- cpp_utils/I2C.cpp | 6 +++--- cpp_utils/I2C.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index dfcfeff0..97d44141 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -95,7 +95,7 @@ uint8_t I2C::getAddress() const * @param [in] sclPin The pin to use for SCL clock. * @return N/A. */ -void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum) { +void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum, bool pullup) { ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d, clockSpeed=%d, portNum=%d", address, sdaPin, sclPin, clockSpeed, portNum); assert(portNum < I2C_NUM_MAX); m_portNum = portNum; @@ -107,8 +107,8 @@ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t c conf.mode = I2C_MODE_MASTER; conf.sda_io_num = sdaPin; conf.scl_io_num = sclPin; - conf.sda_pullup_en = GPIO_PULLUP_ENABLE; - conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.sda_pullup_en = pullup ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.scl_pullup_en = pullup ? GPIO_PULLUP_ENABLE: GPIO_PULLUP_DISABLE; conf.master.clk_speed = clockSpeed; esp_err_t errRc = ::i2c_param_config(m_portNum, &conf); if (errRc != ESP_OK) { diff --git a/cpp_utils/I2C.h b/cpp_utils/I2C.h index 9886d63a..62872e42 100644 --- a/cpp_utils/I2C.h +++ b/cpp_utils/I2C.h @@ -45,7 +45,7 @@ class I2C { void beginTransaction(); void endTransaction(); uint8_t getAddress() const; - void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0); + void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0, bool pullup = true); void read(uint8_t* bytes, size_t length, bool ack=true); void read(uint8_t* byte, bool ack=true); void scan(); From 7843cebaf4980b58c4dcefc5922020f0aec67f70 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:14:50 -0600 Subject: [PATCH 264/310] Memory.cpp: variable size fixes --- cpp_utils/Memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp index e3be59db..a1ce04d0 100644 --- a/cpp_utils/Memory.cpp +++ b/cpp_utils/Memory.cpp @@ -47,7 +47,7 @@ size_t Memory::m_lastHeapSize = 0; /* STATIC */ void Memory::dumpHeapChange(std::string tag) { size_t currentUsage = heap_caps_get_free_size(MALLOC_CAP_8BIT); - int diff = currentUsage - m_lastHeapSize; + size_t diff = currentUsage - m_lastHeapSize; ESP_LOGD(LOG_TAG, "%s: Heap changed by %d bytes (%d to %d)", tag.c_str(), diff, m_lastHeapSize, currentUsage); m_lastHeapSize = currentUsage; } // dumpHeapChange @@ -69,7 +69,7 @@ size_t Memory::m_lastHeapSize = 0; return; } esp_log_level_set("*", ESP_LOG_NONE); - size_t count = heap_trace_get_count(); + size_t count = (size_t) heap_trace_get_count(); heap_trace_record_t record; printf(">>> dumpRanges\n"); for (size_t i=0; i Date: Sun, 14 Oct 2018 17:24:19 -0600 Subject: [PATCH 265/310] PubSub: pass pdTRUE instead of true --- cpp_utils/PubSubClient.cpp | 7 +++---- cpp_utils/PubSubClient.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp_utils/PubSubClient.cpp b/cpp_utils/PubSubClient.cpp index 8aaca5eb..3d1c365d 100644 --- a/cpp_utils/PubSubClient.cpp +++ b/cpp_utils/PubSubClient.cpp @@ -190,10 +190,10 @@ void PubSubClient::setup(void) { keepAliveTimer = new FreeRTOSTimer((char*) "keepAliveTimer", - (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, true, this, + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, pdTRUE, this, keepAliveTimerMapper); timeoutTimer = new FreeRTOSTimer((char*) "timeoutTimer", - (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, true, this, + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, pdTRUE, this, timeoutTimerMapper); m_task = new PubSubClientTask("PubSubClientTask"); } // setup @@ -404,8 +404,7 @@ bool PubSubClient::connect(){ * @param N/A. * @return Number of received bytes. */ -uint16_t PubSubClient::readPacket() { - +size_t PubSubClient::readPacket() { size_t res = _client->receive(buffer, MQTT_MAX_PACKET_SIZE); if (res > MQTT_MAX_PACKET_SIZE) { diff --git a/cpp_utils/PubSubClient.h b/cpp_utils/PubSubClient.h index 8e56ca59..fe82af03 100644 --- a/cpp_utils/PubSubClient.h +++ b/cpp_utils/PubSubClient.h @@ -160,7 +160,7 @@ class PubSubClient { MQTT_CALLBACK_SIGNATURE; void setup (void); - uint16_t readPacket(); + size_t readPacket(); bool write (uint8_t header, uint8_t* buf, uint16_t length); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); void parseData (mqtt_message* msg, uint16_t len); From db61e20ae6871fab172fd31cfc6348e21b212cbb Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:25:31 -0600 Subject: [PATCH 266/310] Fixed ternary operator spacing --- cpp_utils/I2C.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 97d44141..dbf2a3da 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -108,7 +108,7 @@ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t c conf.sda_io_num = sdaPin; conf.scl_io_num = sclPin; conf.sda_pullup_en = pullup ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; - conf.scl_pullup_en = pullup ? GPIO_PULLUP_ENABLE: GPIO_PULLUP_DISABLE; + conf.scl_pullup_en = pullup ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; conf.master.clk_speed = clockSpeed; esp_err_t errRc = ::i2c_param_config(m_portNum, &conf); if (errRc != ESP_OK) { From 61bb6ed15cb8414a7d13747478aefc346446aba6 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:28:49 -0600 Subject: [PATCH 267/310] PWM: idle level is bool --- cpp_utils/PWM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/PWM.cpp b/cpp_utils/PWM.cpp index f7119c4a..0dbf73fe 100644 --- a/cpp_utils/PWM.cpp +++ b/cpp_utils/PWM.cpp @@ -150,5 +150,5 @@ void PWM::setFrequency(uint32_t freq) { * @return N/A. */ void PWM::stop(bool idleLevel) { - ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, m_channel, idleLevel)); + ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, m_channel, idleLevel ? 1 : 0)); } // stop From 871a946968168c24326d795af5568af0f93dcbf9 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:31:41 -0600 Subject: [PATCH 268/310] WebSocket: Opcodes are uint8_t --- cpp_utils/WebSocket.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index e7fc05fb..99172ac6 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -21,12 +21,12 @@ extern "C" { static const char* LOG_TAG = "WebSocket"; // WebSocket op codes as found in a WebSocket frame. -static const int OPCODE_CONTINUE = 0x00; -static const int OPCODE_TEXT = 0x01; -static const int OPCODE_BINARY = 0x02; -static const int OPCODE_CLOSE = 0x08; -static const int OPCODE_PING = 0x09; -static const int OPCODE_PONG = 0x0a; +static const uint8_t OPCODE_CONTINUE = 0x00; +static const uint8_t OPCODE_TEXT = 0x01; +static const uint8_t OPCODE_BINARY = 0x02; +static const uint8_t OPCODE_CLOSE = 0x08; +static const uint8_t OPCODE_PING = 0x09; +static const uint8_t OPCODE_PONG = 0x0a; // Structure definition for the WebSocket frame. @@ -119,7 +119,7 @@ class WebSocketReader: public Task { break; } ESP_LOGD("WebSocketReader", "Waiting on socket data for socket %s", peerSocket.toString().c_str()); - int length = peerSocket.receive((uint8_t*)&frame, sizeof(frame), true); // Read exact + size_t length = peerSocket.receive((uint8_t*)&frame, sizeof(frame), true); // Read exact if (length != sizeof(frame)) { ESP_LOGD(LOG_TAG, "Socket read error"); pWebSocket->close(); @@ -476,7 +476,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { // We wish to refill the buffer. We want to read data from the socket. We want to read either // the size of the buffer to fill it or the maximum number of bytes remaining to be read. // We will choose which ever is smaller as the number of bytes to read into the buffer. - int remainingBytes = getRecordSize()-m_sizeRead; + size_t remainingBytes = getRecordSize()-m_sizeRead; size_t sizeToRead; if (remainingBytes < m_bufferSize) { sizeToRead = remainingBytes; @@ -485,7 +485,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { } ESP_LOGD("WebSocketInputRecordStreambuf", "- getting next buffer of data; size request: %d", sizeToRead); - int bytesRead = m_socket.receive((uint8_t*)m_buffer, sizeToRead, true); + size_t bytesRead = m_socket.receive((uint8_t*)m_buffer, sizeToRead, true); if (bytesRead == 0) { ESP_LOGD("WebSocketInputRecordStreambuf", "<< underflow: Read 0 bytes"); return EOF; From e215f9d70730bef3336f103b73fb8eb12ff90611 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:39:54 -0600 Subject: [PATCH 269/310] PCF8574 & PCF8575: i2c on heap --- cpp_utils/PCF8574.cpp | 18 ++++++++++-------- cpp_utils/PCF8574.h | 2 +- cpp_utils/PCF8575.cpp | 22 ++++++++++++---------- cpp_utils/PCF8575.h | 4 ++-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/cpp_utils/PCF8574.cpp b/cpp_utils/PCF8574.cpp index 4d850a8b..3f56807c 100644 --- a/cpp_utils/PCF8574.cpp +++ b/cpp_utils/PCF8574.cpp @@ -18,7 +18,8 @@ * @param [in] address The %I2C address of the device on the %I2C bus. */ PCF8574::PCF8574(uint8_t address) { - i2c.setAddress(address); + i2c = new I2C(); + i2c->setAddress(address); lastWrite = 0; } @@ -26,6 +27,7 @@ PCF8574::PCF8574(uint8_t address) { * @brief Class instance destructor. */ PCF8574::~PCF8574() { + delete i2c; } @@ -35,9 +37,9 @@ PCF8574::~PCF8574() { */ uint8_t PCF8574::read() { uint8_t value; - i2c.beginTransaction(); - i2c.read(&value,true); - i2c.endTransaction(); + i2c->beginTransaction(); + i2c->read(&value,true); + i2c->endTransaction(); return value; } // read @@ -66,9 +68,9 @@ void PCF8574::write(uint8_t value) { if (invert) { value = ~value; } - i2c.beginTransaction(); - i2c.write(value, true); - i2c.endTransaction(); + i2c->beginTransaction(); + i2c->write(value, true); + i2c->endTransaction(); lastWrite = value; } // write @@ -117,5 +119,5 @@ void PCF8574::setInvert(bool value) { * @param [in] clkPin The pin to use for the %I2C CLK functions. */ void PCF8574::init(gpio_num_t sdaPin, gpio_num_t clkPin) { - i2c.init(0, sdaPin, clkPin); + i2c->init(0, sdaPin, clkPin); } // init diff --git a/cpp_utils/PCF8574.h b/cpp_utils/PCF8574.h index 15b11da9..777d17f8 100644 --- a/cpp_utils/PCF8574.h +++ b/cpp_utils/PCF8574.h @@ -29,7 +29,7 @@ class PCF8574 { void writeBit(uint8_t bit, bool value); private: - I2C i2c = I2C(); + I2C* i2c; uint8_t lastWrite; bool invert = false; }; diff --git a/cpp_utils/PCF8575.cpp b/cpp_utils/PCF8575.cpp index 3714abb1..87435654 100644 --- a/cpp_utils/PCF8575.cpp +++ b/cpp_utils/PCF8575.cpp @@ -18,7 +18,8 @@ * @param [in] address The %I2C address of the device on the %I2C bus. */ PCF8575::PCF8575(uint8_t address) { - i2c.setAddress(address); + i2c = new I2C(); + i2c->setAddress(address); m_lastWrite = 0; } @@ -26,6 +27,7 @@ PCF8575::PCF8575(uint8_t address) { * @brief Class instance destructor. */ PCF8575::~PCF8575() { + delete i2c; } @@ -35,10 +37,10 @@ PCF8575::~PCF8575() { */ uint16_t PCF8575::read() { uint16_t value; - i2c.beginTransaction(); - i2c.read((uint8_t*)&value,true); - i2c.read(((uint8_t*)&value) + 1,true); - i2c.endTransaction(); + i2c->beginTransaction(); + i2c->read((uint8_t*)&value,true); + i2c->read(((uint8_t*)&value) + 1,true); + i2c->endTransaction(); return value; } // read @@ -67,10 +69,10 @@ void PCF8575::write(uint16_t value) { if (invert) { value = ~value; } - i2c.beginTransaction(); - i2c.write(value & 0xff, true); - i2c.write((value >> 8) & 0xff, true); - i2c.endTransaction(); + i2c->beginTransaction(); + i2c->write(value & 0xff, true); + i2c->write((value >> 8) & 0xff, true); + i2c->endTransaction(); m_lastWrite = value; } // write @@ -119,5 +121,5 @@ void PCF8575::setInvert(bool value) { * @param [in] clkPin The pin to use for the %I2C CLK functions. */ void PCF8575::init(gpio_num_t sdaPin, gpio_num_t clkPin) { - i2c.init(0, sdaPin, clkPin); + i2c->init(0, sdaPin, clkPin); } // init diff --git a/cpp_utils/PCF8575.h b/cpp_utils/PCF8575.h index 467fae6c..c37eed8b 100644 --- a/cpp_utils/PCF8575.h +++ b/cpp_utils/PCF8575.h @@ -29,8 +29,8 @@ class PCF8575 { void writeBit(uint16_t bit, bool value); private: - I2C i2c = I2C(); - uint8_t m_lastWrite; + I2C* i2c; + uint16_t m_lastWrite; bool invert = false; }; From bd24fabae4e76e58d660225a3fdfa9f27cfb1639 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 17:41:31 -0600 Subject: [PATCH 270/310] Release memory from CLASSIC_BT when not enabled --- cpp_utils/BLEDevice.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 157074d4..dcba79eb 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -354,13 +354,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; } #ifndef CLASSIC_BT_ENABLED - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue - errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); #else errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (errRc != ESP_OK) { From b50dec3621f6e4acb02140b3da078542f35fb6c9 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 18:18:50 -0600 Subject: [PATCH 271/310] Fix array dereferencing --- cpp_utils/BLEAdvertising.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index ea3825ac..d4fd6332 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -300,7 +300,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16)); + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); break; } From 2dcd6aa02cd353f1c5e0efbef4c00cb7b4989981 Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 18:34:12 -0600 Subject: [PATCH 272/310] Fix HttpResponse line mangling --- cpp_utils/HttpResponse.cpp | 4 ++-- cpp_utils/HttpResponse.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index c6e9683c..46f3e9bd 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -26,9 +26,9 @@ const int HttpResponse::HTTP_STATUS_NOT_IMPLEMENTED = 501; const int HttpResponse::HTTP_STATUS_SERVICE_UNAVAILABLE = 503; static std::string lineTerminator = "\r\n"; +HttpResponse::HttpResponse(HttpRequest* request) { m_request = request; m_status = 200; -HttpResponse::HttpResponse(HttpRequest* request) { m_headerCommitted = false; // We have not yet sent a header. } @@ -125,7 +125,7 @@ void HttpResponse::sendFile(std::string fileName, size_t bufSize) { ESP_LOGI(LOG_TAG, "Opening file: %s", fileName.c_str()); std::ifstream ifStream; ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. - + // If we failed to open the requested file, then it probably didn't exist so return a not found. if (!ifStream.is_open()) { ESP_LOGE(LOG_TAG, "Unable to open file %s for reading", fileName.c_str()); diff --git a/cpp_utils/HttpResponse.h b/cpp_utils/HttpResponse.h index cc7be388..5dfaa976 100644 --- a/cpp_utils/HttpResponse.h +++ b/cpp_utils/HttpResponse.h @@ -39,6 +39,7 @@ class HttpResponse { void sendData(uint8_t* pData, size_t size); // Send data to the client. void setStatus(int status, std::string message); // Set the response status. void sendFile(std::string fileName, size_t bufSize = 4 * 1024); // Send file contents if exists. + private: bool m_headerCommitted; // Has the header been sent? HttpRequest* m_request; // The request associated with this response. From 4cfef4d077945ee3429de6d7416f4635432e35ea Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 18:34:39 -0600 Subject: [PATCH 273/310] Change return type of bitSize() to uint8_t --- cpp_utils/BLEUUID.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index 5fb7795b..e81dd130 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -24,7 +24,7 @@ class BLEUUID { BLEUUID(uint8_t* pData, size_t size, bool msbFirst); BLEUUID(esp_gatt_id_t gattId); BLEUUID(); - int bitSize(); // Get the number of bits in this uuid. + uint8_t bitSize(); // Get the number of bits in this uuid. bool equals(BLEUUID uuid); esp_bt_uuid_t* getNative(); BLEUUID to128(); From d14071a74898ea98d224cd862de04507f8c728de Mon Sep 17 00:00:00 2001 From: toxuin Date: Sun, 14 Oct 2018 18:35:03 -0600 Subject: [PATCH 274/310] Fix line mangling on merge Force gcc to ignore unused-but-set-parameter Fix line misplacement by merge Cast string data to uint8_t pointer Fix line mangling on merge Fix line mangling on merge Fix line mangling on merge --- cpp_utils/BLECharacteristicMap.cpp | 14 ++++---------- cpp_utils/BLERemoteDescriptor.cpp | 2 +- cpp_utils/BLERemoteService.cpp | 1 + cpp_utils/MFRC522.cpp | 9 ++++----- cpp_utils/SockServ.cpp | 30 +++++++++++++++--------------- cpp_utils/System.cpp | 10 ++++++---- cpp_utils/WS2812.cpp | 2 +- 7 files changed, 32 insertions(+), 36 deletions(-) diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index 8e7d097d..d73aae99 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -81,12 +81,9 @@ BLECharacteristic* BLECharacteristicMap::getNext() { * @param [in] gatts_if * @param [in] param */ -void BLECharacteristicMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { +void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { + for (auto& myPair : m_uuidMap) { myPair.first->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent @@ -98,8 +95,7 @@ void BLECharacteristicMap::handleGATTServerEvent( * @param [in] characteristic The characteristic to cache. * @return N/A. */ -void BLECharacteristicMap::setByHandle(uint16_t handle, - BLECharacteristic* characteristic) { +void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) { m_handleMap.insert(std::pair(handle, characteristic)); } // setByHandle @@ -110,9 +106,7 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, * @param [in] characteristic The characteristic to cache. * @return N/A. */ -void BLECharacteristicMap::setByUUID( - BLEUUID uuid) { - BLECharacteristic* pCharacteristic, +void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) { m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); } // setByUUID diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 4947976b..ea7eeebd 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -161,7 +161,7 @@ void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response * @param [in] response True if we expect a response. */ void BLERemoteDescriptor::writeValue(std::string newValue, bool response) { - writeValue(newValue.data(), newValue.length(), response); + writeValue((uint8_t*) newValue.data(), newValue.length(), response); } // writeValue diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index a1bf06e7..d632cff5 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -233,6 +233,7 @@ std::map* BLERemoteService::getCharacteri * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID */ void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { +#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" pCharacteristicMap = &m_characteristicMapByHandle; } // Get the characteristics map. diff --git a/cpp_utils/MFRC522.cpp b/cpp_utils/MFRC522.cpp index 6feb57c1..4082d4ac 100644 --- a/cpp_utils/MFRC522.cpp +++ b/cpp_utils/MFRC522.cpp @@ -146,7 +146,7 @@ void MFRC522::PCD_ClearRegisterBitMask(PCD_Register reg, byte mask) { * @param result Out: Pointer to result buffer. Result is written to result[0..1], low byte first. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte *data, byte length, byte *result) { +MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte* data, byte length, byte* result) { PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit PCD_WriteRegister(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization @@ -160,7 +160,6 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte *data, byte length, byte *res for (uint16_t i = 5000; i > 0; i--) { // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved byte n = PCD_ReadRegister(DivIrqReg); - if (n & 0x04) { // CRCIRq bit set - calculation done if (n & 0x04) { // CRCIRq bit set - calculation done PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. // Transfer the result from the registers to the result buffer @@ -403,8 +402,8 @@ bool MFRC522::PCD_PerformSelfTest() { * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ - byte waitIRq = 0x30; // RxIRq and IdleIRq MFRC522::StatusCode MFRC522::PCD_TransceiveData(byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { + byte waitIRq = 0x30; // RxIRq and IdleIRq return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); } // End PCD_TransceiveData() @@ -426,8 +425,8 @@ MFRC522::StatusCode MFRC522::PCD_TransceiveData(byte* sendData, byte sendLen, by */ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC(byte command, byte waitIRq, byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { // Prepare values for BitFramingReg - byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] byte txLastBits = validBits ? *validBits : (byte) 0; + byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits @@ -808,8 +807,8 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { * @param uid Pointer to Uid struct. The first 4 bytes of the UID is used. * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. */ - byte waitIRq = 0x10; // IdleIRq MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key* key, Uid* uid) { + byte waitIRq = 0x10; // IdleIRq // Build command buffer byte sendData[12]; diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 32b494ab..38ecc823 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -30,7 +30,6 @@ static const char* LOG_TAG = "SockServ"; */ SockServ::SockServ(uint16_t port) : SockServ() { this->m_port = port; - } // SockServ @@ -62,8 +61,8 @@ SockServ::~SockServ() { */ /* static */ void SockServ::acceptTask(void* data) { SockServ* pSockServ = (SockServ*) data; - try { - while (true) { + while (true) { + try { ESP_LOGD(LOG_TAG, "Waiting on accept"); Socket tempSock = pSockServ->m_serverSocket.accept(); if (!tempSock.isValid()) continue; @@ -71,11 +70,12 @@ SockServ::~SockServ() { pSockServ->m_clientSet.insert(tempSock); xQueueSendToBack(pSockServ->m_acceptQueue, &tempSock, portMAX_DELAY); pSockServ->m_clientSemaphore.give(); + } catch (std::exception e) { + ESP_LOGD(LOG_TAG, "acceptTask ending"); + pSockServ->m_clientSemaphore.give(); // Wake up any waiting clients. + FreeRTOS::deleteTask(); + break; } - } catch(std::exception e) { - ESP_LOGD(LOG_TAG, "acceptTask ending"); - pSockServ->m_clientSemaphore.give(); // Wake up any waiting clients. - FreeRTOS::deleteTask(); } } // acceptTask @@ -114,12 +114,12 @@ bool SockServ::getSSL() { * @return The amount of data returned or 0 if there was an error. */ size_t SockServ::receiveData(Socket s, void* pData, size_t maxData) { - int rc = s.receive((uint8_t*) pData, maxData); + size_t rc = s.receive((uint8_t*) pData, maxData); if (rc == -1) { ESP_LOGE(LOG_TAG, "recv(): %s", strerror(errno)); return 0; } - return (size_t) rc; + return rc; } // receiveData @@ -190,7 +190,7 @@ Socket SockServ::waitForData(std::set& socketSet) { fd_set readSet; int maxFd = -1; - for ( auto it = socketSet.begin(); it != socketSet.end(); ++it) { + for (auto it = socketSet.begin(); it != socketSet.end(); ++it) { FD_SET(it->getFD(), &readSet); if (it->getFD() > maxFd) { maxFd = it->getFD(); @@ -198,11 +198,11 @@ Socket SockServ::waitForData(std::set& socketSet) { } // End for int rc = ::select( - &readSet, // Set of read sockets - nullptr, // Set of write sockets - nullptr, // Set of exception sockets - nullptr // Timeout - maxFd + 1, // Number of sockets to scan + maxFd+1, // Number of sockets to scan + &readSet, // Set of read sockets + nullptr, // Set of write sockets + nullptr, // Set of exception sockets + nullptr // Timeout ); if (rc == -1) { ESP_LOGE(LOG_TAG, "Error with select"); diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index 3b774db4..2e24f88b 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -47,6 +47,9 @@ typedef volatile struct { } pin_ctrl; // The 36 exposed pads. + io_mux_reg_t pad_gpio36; // GPIO36 + io_mux_reg_t pad_gpio37; // GPIO37 + io_mux_reg_t pad_gpio38; // GPIO38 io_mux_reg_t pad_gpio39; // GPIO39 io_mux_reg_t pad_gpio34; // GPIO34 io_mux_reg_t pad_gpio35; // GPIO35 @@ -64,7 +67,6 @@ typedef volatile struct { io_mux_reg_t pad_gpio4; // GPIO4 io_mux_reg_t pad_gpio16; // GPIO16 io_mux_reg_t pad_gpio17; // GPIO17 - io_mux_reg_t pad_gpio37; // GPIO37 io_mux_reg_t pad_sd_data2; // GPIO9 io_mux_reg_t pad_sd_data3; // GPIO10 io_mux_reg_t pad_sd_cmd; // GPIO11 @@ -406,16 +408,16 @@ const static char* outSignalStrings[] = { for (uint8_t i = 0; i < numPins; i++) { const char *signal; if (GPIO.func_out_sel_cfg[i].func_sel == 256) { - signal = (char *)"[GPIO]"; + signal = (char*) "[GPIO]"; } else if (GPIO.func_out_sel_cfg[i].func_sel == 257) { - signal = (char *)"N/A"; + signal = (char*) "N/A"; } else { signal = outSignalStrings[GPIO.func_out_sel_cfg[i].func_sel]; } printf("%2d %4d %s\n", i, GPIO.func_out_sel_cfg[i].func_sel, signal); const io_mux_reg_t* io_mux = gpioToIoMux(i); if (GPIO.func_out_sel_cfg[i].func_sel == 256 && io_mux != nullptr) { - printf("0x%x - function: %d, ie: %d\n", (uint32_t)io_mux, io_mux->mcu_sel + 1, io_mux->func_ie); + printf("0x%x - function: %d, ie: %d\n", (uint32_t) io_mux, io_mux->mcu_sel + 1, io_mux->func_ie); } } diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index c0f622d7..0b8a3cbe 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -158,7 +158,7 @@ void WS2812::show() { (getChannelValueByType(this->colorOrder[2], this->pixels[i])); ESP_LOGD(LOG_TAG, "Pixel value: %x", currentPixel); - for (uint8_t j = 23; j > =0; j--) { + for (uint8_t j = 23; j >= 0; j--) { // We have 24 bits of data representing the red, green amd blue channels. The value of the // 24 bits to output is in the variable current_pixel. We now need to stream this value // through RMT in most significant bit first. To do this, we iterate through each of the 24 From 5a97e6802ec820036a016450508fba7f54a61224 Mon Sep 17 00:00:00 2001 From: Friedemann Stoffregen Date: Mon, 15 Oct 2018 13:05:16 +0200 Subject: [PATCH 275/310] added getServiceCount() to BLEServer.cpp --- cpp_utils/BLEServer.cpp | 15 +++++++++++++++ cpp_utils/BLEServer.h | 1 + 2 files changed, 16 insertions(+) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 7e7007b2..72cb5570 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -115,6 +115,21 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { } +/** + * @brief Returns the amount of services registered to this server + * @return The amount of registered services + */ +int BLEServer::getServiceCount() { + int count = 0; + if(m_serviceMap.getFirst() == nullptr) return 0; + while(m_serviceMap.getNext() != nullptr){ + count++; + } + + return count; +} + + /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. * diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index e3362b75..83439d2a 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -43,6 +43,7 @@ class BLEServiceMap { BLEService* getFirst(); BLEService* getNext(); void removeService(BLEService* service); + int getServiceCount(); private: std::map m_handleMap; From 6a8c821695ea9d2a178d808378bad5f206a105c4 Mon Sep 17 00:00:00 2001 From: Friedemann Stoffregen Date: Mon, 15 Oct 2018 13:19:53 +0200 Subject: [PATCH 276/310] improved getServiceCount() --- cpp_utils/BLEServer.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 72cb5570..31c64d3d 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -120,13 +120,7 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @return The amount of registered services */ int BLEServer::getServiceCount() { - int count = 0; - if(m_serviceMap.getFirst() == nullptr) return 0; - while(m_serviceMap.getNext() != nullptr){ - count++; - } - - return count; + return m_serviceMap.size(); } From 693e2c93c58204f91acc1ab096b2315bdc26fcd5 Mon Sep 17 00:00:00 2001 From: Friedemann Stoffregen Date: Wed, 17 Oct 2018 08:45:30 +0200 Subject: [PATCH 277/310] improved BLEServer::getServiceCount() and added parameter for optional count of default services --- cpp_utils/BLEServer.cpp | 6 +++++- cpp_utils/BLEServer.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 31c64d3d..05d29772 100644 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -117,9 +117,13 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { /** * @brief Returns the amount of services registered to this server + * @param [in] includeDefaultServices Add the amount of default BluetoothLE services defined by the BLE standard * @return The amount of registered services */ -int BLEServer::getServiceCount() { +int BLEServer::getServiceCount(bool includeDefaultServices = false) { + if(includeDefaultServices){ + return m_serviceMap.size() + 2; + } return m_serviceMap.size(); } diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 83439d2a..df612848 100644 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -43,7 +43,6 @@ class BLEServiceMap { BLEService* getFirst(); BLEService* getNext(); void removeService(BLEService* service); - int getServiceCount(); private: std::map m_handleMap; @@ -67,6 +66,7 @@ class BLEServer { BLEService* getServiceByUUID(const char* uuid); BLEService* getServiceByUUID(BLEUUID uuid); void removeService(BLEService* service); + int getServiceCount(bool includeDefaultServices = false); private: BLEServer(); From 96f76c02146811ed884d992e1fec621979dec583 Mon Sep 17 00:00:00 2001 From: Friedemann Stoffregen Date: Wed, 17 Oct 2018 09:58:39 +0200 Subject: [PATCH 278/310] fixed some issues --- cpp_utils/BLEServer.cpp | 6 +++--- cpp_utils/BLEServer.h | 4 +++- cpp_utils/BLEServiceMap.cpp | 30 +++++++++++++++++++++--------- 3 files changed, 27 insertions(+), 13 deletions(-) mode change 100644 => 100755 cpp_utils/BLEServer.cpp mode change 100644 => 100755 cpp_utils/BLEServer.h mode change 100644 => 100755 cpp_utils/BLEServiceMap.cpp diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp old mode 100644 new mode 100755 index 05d29772..b40cd345 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -120,11 +120,11 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @param [in] includeDefaultServices Add the amount of default BluetoothLE services defined by the BLE standard * @return The amount of registered services */ -int BLEServer::getServiceCount(bool includeDefaultServices = false) { +int BLEServer::getServiceCount(bool includeDefaultServices) { if(includeDefaultServices){ - return m_serviceMap.size() + 2; + return m_serviceMap.getRegisteredServiceCount() + 2; } - return m_serviceMap.size(); + return m_serviceMap.getRegisteredServiceCount(); } diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h old mode 100644 new mode 100755 index df612848..799eb25d --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -43,6 +43,8 @@ class BLEServiceMap { BLEService* getFirst(); BLEService* getNext(); void removeService(BLEService* service); + int getRegisteredServiceCount(); + private: std::map m_handleMap; @@ -66,7 +68,7 @@ class BLEServer { BLEService* getServiceByUUID(const char* uuid); BLEService* getServiceByUUID(BLEUUID uuid); void removeService(BLEService* service); - int getServiceCount(bool includeDefaultServices = false); + int getServiceCount(bool includeDefaultServices); private: BLEServer(); diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp old mode 100644 new mode 100755 index d8610a85..aa371f47 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -17,9 +17,9 @@ * @return The characteristic. */ BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } - + /** * @brief Return the service by UUID. * @param [in] UUID The UUID to look up the service. @@ -53,8 +53,8 @@ BLEService* BLEServiceMap::getByHandle(uint16_t handle) { * @return N/A. */ void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService* service) { - m_uuidMap.insert(std::pair(service, uuid.toString())); + BLEService *service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); } // setByUUID @@ -66,7 +66,7 @@ void BLEServiceMap::setByUUID(BLEUUID uuid, */ void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); + m_handleMap.insert(std::pair(handle, service)); } // setByHandle @@ -86,7 +86,7 @@ std::string BLEServiceMap::toString() { void BLEServiceMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { + esp_ble_gatts_cb_param_t *param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { myPair.first->handleGATTServerEvent(event, gatts_if, param); @@ -99,7 +99,9 @@ void BLEServiceMap::handleGATTServerEvent( */ BLEService* BLEServiceMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) return nullptr; + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -110,7 +112,9 @@ BLEService* BLEServiceMap::getFirst() { * @return The next service in the map. */ BLEService* BLEServiceMap::getNext() { - if (m_iterator == m_uuidMap.end()) return nullptr; + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -120,9 +124,17 @@ BLEService* BLEServiceMap::getNext() { * @brief Removes service from maps. * @return N/A. */ -void BLEServiceMap::removeService(BLEService* service) { +void BLEServiceMap::removeService(BLEService *service){ m_handleMap.erase(service->getHandle()); m_uuidMap.erase(service); } // removeService +/** + * @brief Returns the amount of registered services + * @return amount of registered services + */ +int BLEServiceMap::getRegisteredServiceCount(){ + return m_handleMap.size(); +} + #endif /* CONFIG_BT_ENABLED */ From 47563cb00715c7ccced11f58cbeb813b394f2f5f Mon Sep 17 00:00:00 2001 From: toxuin Date: Wed, 17 Oct 2018 20:08:01 -0600 Subject: [PATCH 279/310] Fix codestyle --- .gitignore | 8 +++++++- cpp_utils/BLEServer.cpp | 2 +- cpp_utils/BLEServiceMap.cpp | 26 ++++++++++---------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 099e1f7c..15d2ae94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ .project .cproject .settings/ -/esp32-snippets/.vs + +# IDEs +.vs/ +.idea/ + +# CMake +cmake-build-debug/ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index b40cd345..e5c675b8 100755 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -121,7 +121,7 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { * @return The amount of registered services */ int BLEServer::getServiceCount(bool includeDefaultServices) { - if(includeDefaultServices){ + if (includeDefaultServices) { return m_serviceMap.getRegisteredServiceCount() + 2; } return m_serviceMap.getRegisteredServiceCount(); diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index aa371f47..c749be16 100755 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -17,9 +17,9 @@ * @return The characteristic. */ BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } - + /** * @brief Return the service by UUID. * @param [in] UUID The UUID to look up the service. @@ -52,9 +52,8 @@ BLEService* BLEServiceMap::getByHandle(uint16_t handle) { * @param [in] characteristic The service to cache. * @return N/A. */ -void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService *service) { - m_uuidMap.insert(std::pair(service, uuid.toString())); +void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); } // setByUUID @@ -64,9 +63,8 @@ void BLEServiceMap::setByUUID(BLEUUID uuid, * @param [in] service The service to cache. * @return N/A. */ -void BLEServiceMap::setByHandle(uint16_t handle, - BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); +void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { + m_handleMap.insert(std::pair(handle, service)); } // setByHandle @@ -86,7 +84,7 @@ std::string BLEServiceMap::toString() { void BLEServiceMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { + esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { myPair.first->handleGATTServerEvent(event, gatts_if, param); @@ -99,9 +97,7 @@ void BLEServiceMap::handleGATTServerEvent( */ BLEService* BLEServiceMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -112,9 +108,7 @@ BLEService* BLEServiceMap::getFirst() { * @return The next service in the map. */ BLEService* BLEServiceMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLEService* pRet = m_iterator->first; m_iterator++; return pRet; @@ -124,7 +118,7 @@ BLEService* BLEServiceMap::getNext() { * @brief Removes service from maps. * @return N/A. */ -void BLEServiceMap::removeService(BLEService *service){ +void BLEServiceMap::removeService(BLEService* service) { m_handleMap.erase(service->getHandle()); m_uuidMap.erase(service); } // removeService From 125ceb3df67e92cc3d5c639240adaa9601d2d186 Mon Sep 17 00:00:00 2001 From: toxuin Date: Wed, 17 Oct 2018 20:49:25 -0600 Subject: [PATCH 280/310] Change local includes from brackets to quotes --- cpp_utils/BLESecurity.cpp | 2 +- cpp_utils/File.cpp | 2 +- cpp_utils/GeneralUtils.cpp | 4 ++-- cpp_utils/MFRC522.cpp | 4 ++-- cpp_utils/SockServ.cpp | 4 ++-- cpp_utils/TFTP.cpp | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp index 114f630d..921f5424 100644 --- a/cpp_utils/BLESecurity.cpp +++ b/cpp_utils/BLESecurity.cpp @@ -5,7 +5,7 @@ * Author: chegewara */ -#include +#include "BLESecurity.h" #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) diff --git a/cpp_utils/File.cpp b/cpp_utils/File.cpp index 3b245a85..3ac4672f 100644 --- a/cpp_utils/File.cpp +++ b/cpp_utils/File.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "GeneralUtils.h" static const char* LOG_TAG = "File"; /** diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 80c5cc6e..8d58d4eb 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include "FreeRTOS.h" #include #include #include @@ -440,7 +440,7 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { * @brief Convert a wifi_err_reason_t code to a string. * @param [in] errCode The errCode to be converted. * @return A string representation of the error code. - * + * * @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h. */ const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { diff --git a/cpp_utils/MFRC522.cpp b/cpp_utils/MFRC522.cpp index 4082d4ac..17ca1754 100644 --- a/cpp_utils/MFRC522.cpp +++ b/cpp_utils/MFRC522.cpp @@ -18,8 +18,8 @@ #include "MFRC522.h" #include "MFRC522Debug.h" -#include -#include +#include "FreeRTOS.h" +#include "GPIO.h" #include #include #include diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 38ecc823..020ed096 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -7,15 +7,15 @@ #include #include -#include #include #include - #include + #include #include #include "sdkconfig.h" +#include "FreeRTOS.h" #include "SockServ.h" #include "Socket.h" diff --git a/cpp_utils/TFTP.cpp b/cpp_utils/TFTP.cpp index d554bd45..b1591a9f 100644 --- a/cpp_utils/TFTP.cpp +++ b/cpp_utils/TFTP.cpp @@ -9,13 +9,13 @@ #include "TFTP.h" #include -#include -#include +#include "FreeRTOS.h" +#include "GeneralUtils.h" #include #include #include #include -#include +#include "Socket.h" #include "sdkconfig.h" From 0ebec95c1f8f49467182d9de1c27caea0c828ddb Mon Sep 17 00:00:00 2001 From: Ryotaro Onuki Date: Thu, 18 Oct 2018 14:03:36 +0900 Subject: [PATCH 281/310] fix an infinite loop in WS2812.cpp just a simple mistake --- cpp_utils/WS2812.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index 0b8a3cbe..af4bc512 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -158,7 +158,7 @@ void WS2812::show() { (getChannelValueByType(this->colorOrder[2], this->pixels[i])); ESP_LOGD(LOG_TAG, "Pixel value: %x", currentPixel); - for (uint8_t j = 23; j >= 0; j--) { + for (int8_t j = 23; j >= 0; j--) { // We have 24 bits of data representing the red, green amd blue channels. The value of the // 24 bits to output is in the variable current_pixel. We now need to stream this value // through RMT in most significant bit first. To do this, we iterate through each of the 24 From 913abdffc3186d915f341be24e12d9bbacfdb354 Mon Sep 17 00:00:00 2001 From: Paul Breugnot Date: Sun, 11 Nov 2018 11:17:55 +0100 Subject: [PATCH 282/310] Add slave select and release when reading / writting registers --- cpp_utils/MFRC522.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cpp_utils/MFRC522.cpp b/cpp_utils/MFRC522.cpp index 17ca1754..940c170c 100644 --- a/cpp_utils/MFRC522.cpp +++ b/cpp_utils/MFRC522.cpp @@ -48,7 +48,11 @@ void MFRC522::PCD_WriteRegister(PCD_Register reg, byte value) { uint8_t data[2]; data[0] = reg; data[1] = value; + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transfer(data, 2); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + } // End PCD_WriteRegister() @@ -63,7 +67,11 @@ void MFRC522::PCD_WriteRegister(PCD_Register reg, byte count, byte* values) { uint8_t* pData = new uint8_t[count + 1]; pData[0] = reg; memcpy(pData + 1, values, count); + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transfer(pData, count + 1); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + delete[] pData; } // End PCD_WriteRegister() @@ -77,7 +85,11 @@ byte MFRC522::PCD_ReadRegister(PCD_Register reg) { uint8_t data[2]; data[0] = reg | 0x80; data[1] = 0; + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transfer(data, 2); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + return data[1]; } // End PCD_ReadRegister() @@ -97,6 +109,8 @@ void MFRC522::PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte byte address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. byte index = 0; // Index in values array. count--; // One read is performed outside of the loop + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transferByte(address); // Tell MFRC522 which address we want to read if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] // Create bit mask for bit positions rxAlign..7 @@ -112,6 +126,7 @@ void MFRC522::PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte index++; } values[index] = m_spi.transferByte(0); // Read the final byte. Send 0 to stop reading. + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave } // End PCD_ReadRegister() @@ -181,6 +196,8 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte* data, byte length, byte* res */ void MFRC522::PCD_Init() { //m_spi.setHost(VSPI_HOST); + + ESP32CPP::GPIO::setOutput((gpio_num_t)_chipSelectPin); m_spi.init(); bool hardReset = false; From 5cc6294fa74125188d062d208076e0f1d3d2373d Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 13 Nov 2018 17:43:04 +0100 Subject: [PATCH 283/310] Revert "Release memory from CLASSIC_BT when not enabled" --- cpp_utils/BLEDevice.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index fd86abe0..77380d2b 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -350,7 +350,13 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; } #ifndef CLASSIC_BT_ENABLED - esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } #else errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (errRc != ESP_OK) { From e8b5df49c857ddc07212db3f37dd0b9bae7f7e13 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 14 Nov 2018 18:52:36 +0100 Subject: [PATCH 284/310] fix memory leak, add multi connection features --- cpp_utils/BLEScan.cpp | 116 ++++++++++++++++++++++++++---------------- cpp_utils/BLEScan.h | 17 ++++--- 2 files changed, 82 insertions(+), 51 deletions(-) diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 25b60a0b..7164626b 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -48,22 +48,21 @@ void BLEScan::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { - switch (event) { - - // ESP_GAP_BLE_SCAN_RESULT_EVT - // --------------------------- - // scan_rst: - // esp_gap_search_evt_t search_evt - // esp_bd_addr_t bda - // esp_bt_dev_type_t dev_type - // esp_ble_addr_type_t ble_addr_type - // esp_ble_evt_type_t ble_evt_type - // int rssi - // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] - // int flag - // int num_resps - // uint8_t adv_data_len - // uint8_t scan_rsp_len + switch(event) { + + // --------------------------- + // scan_rst: + // esp_gap_search_evt_t search_evt + // esp_bd_addr_t bda + // esp_bt_dev_type_t dev_type + // esp_ble_addr_type_t ble_addr_type + // esp_ble_evt_type_t ble_evt_type + // int rssi + // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] + // int flag + // int num_resps + // uint8_t adv_data_len + // uint8_t scan_rsp_len case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { @@ -73,11 +72,12 @@ void BLEScan::handleGAPEvent( // Event that indicates that the duration allowed for the search has completed or that we have been // asked to stop. case ESP_GAP_SEARCH_INQ_CMPL_EVT: { + ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT"); m_stopped = true; + m_semaphoreScanEnd.give(); if (m_scanCompleteCB != nullptr) { m_scanCompleteCB(m_scanResults); } - m_semaphoreScanEnd.give(); break; } // ESP_GAP_SEARCH_INQ_CMPL_EVT @@ -90,52 +90,58 @@ void BLEScan::handleGAPEvent( break; } - // Examine our list of previously scanned addresses and, if we found this one already, - // ignore it. +// Examine our list of previously scanned addresses and, if we found this one already, +// ignore it. BLEAddress advertisedAddress(param->scan_rst.bda); bool found = false; - for (uint32_t i = 0; i < m_scanResults.getCount(); i++) { - if (m_scanResults.getDevice(i).getAddress().equals(advertisedAddress)) { - found = true; - break; - } + if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) { + found = true; } + if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str()); + vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we will blocked here break; } // We now construct a model of the advertised device that we have just found for the first // time. - BLEAdvertisedDevice advertisedDevice; - advertisedDevice.setAddress(advertisedAddress); - advertisedDevice.setRSSI(param->scan_rst.rssi); - advertisedDevice.setAdFlag(param->scan_rst.flag); - advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); - advertisedDevice.setScan(this); + // ESP_LOG_BUFFER_HEXDUMP(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG); + // ESP_LOGW(LOG_TAG, "bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type); + BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice(); + advertisedDevice->setAddress(advertisedAddress); + advertisedDevice->setRSSI(param->scan_rst.rssi); + advertisedDevice->setAdFlag(param->scan_rst.flag); + advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len); + advertisedDevice->setScan(this); + advertisedDevice->setAddressType(param->scan_rst.ble_addr_type); - if (m_pAdvertisedDeviceCallbacks) { - m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); } - if (!found) { // If we have previously seen this device, don't record it again. - m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + if (m_pAdvertisedDeviceCallbacks) { + m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } + if(found) + delete advertisedDevice; break; } // ESP_GAP_SEARCH_INQ_RES_EVT - default: + default: { break; + } } // switch - search_evt break; } // ESP_GAP_BLE_SCAN_RESULT_EVT - default: + default: { break; + } // default } // End switch } // gapEventHandler @@ -188,15 +194,23 @@ void BLEScan::setWindow(uint16_t windowMSecs) { * @brief Start scanning. * @param [in] duration The duration in seconds for which to scan. * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @param [in] are we continue scan (true) or we want to clear stored devices (false) * @return True if scan started or false if there was an error. */ -bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) { ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); m_semaphoreScanEnd.take(std::string("start")); m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. - m_scanResults.m_vectorAdvertisedDevices.clear(); + // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals + // then we should not clear map or we will connect the same device few times + if(!is_continue) { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); + } esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); @@ -226,8 +240,8 @@ bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) { * @param [in] duration The duration in seconds for which to scan. * @return The BLEScanResults. */ -BLEScanResults BLEScan::start(uint32_t duration) { - if (start(duration, nullptr)) { +BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) { + if(start(duration, nullptr, is_continue)) { m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. } return m_scanResults; @@ -244,24 +258,31 @@ void BLEScan::stop() { esp_err_t errRc = ::esp_ble_gap_stop_scanning(); m_stopped = true; + m_semaphoreScanEnd.give(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); return; } - m_semaphoreScanEnd.give(); - ESP_LOGD(LOG_TAG, "<< stop()"); } // stop +// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address +void BLEScan::erase(BLEAddress address) { + ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); + BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second; + m_scanResults.m_vectorAdvertisedDevices.erase(address.toString()); + delete advertisedDevice; +} + /** * @brief Dump the scan results to the log. */ void BLEScanResults::dump() { ESP_LOGD(LOG_TAG, ">> Dump scan results:"); - for (uint32_t i = 0; i < getCount(); i++) { + for (int i=0; isecond; + for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) { + dev = *it->second; + if (x==i) break; + x++; + } + return dev; } diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index aec4e3db..ae4663e1 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -11,7 +11,8 @@ #if defined(CONFIG_BT_ENABLED) #include -#include +// #include +#include #include "BLEAdvertisedDevice.h" #include "BLEClient.h" #include "FreeRTOS.h" @@ -37,7 +38,7 @@ class BLEScanResults { private: friend BLEScan; - std::vector m_vectorAdvertisedDevices; + std::map m_vectorAdvertisedDevices; }; /** @@ -53,9 +54,11 @@ class BLEScan { bool wantDuplicates = false); void setInterval(uint16_t intervalMSecs); void setWindow(uint16_t windowMSecs); - bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)); - BLEScanResults start(uint32_t duration); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false); + BLEScanResults start(uint32_t duration, bool is_continue = false); void stop(); + void erase(BLEAddress address); + // void setPower(esp_power_level_t powerLevel); private: BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. @@ -63,12 +66,12 @@ class BLEScan { void handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); - void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t* payload); + void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); esp_ble_scan_params_t m_scan_params; - BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; - bool m_stopped; + BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; + bool m_stopped = true; FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); BLEScanResults m_scanResults; bool m_wantDuplicates; From a4e3d4355ad38c1c3ff57e35fb164128a78c0626 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 14 Nov 2018 19:24:52 +0100 Subject: [PATCH 285/310] Fix issue with parsing scan response #612 --- cpp_utils/BLEAdvertisedDevice.cpp | 25 +++++++++++++++++-------- cpp_utils/BLEAdvertisedDevice.h | 7 +++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 104b2e12..dbda9172 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -228,16 +228,16 @@ bool BLEAdvertisedDevice::haveTXPower() { * * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile */ -void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { +void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) { uint8_t length; uint8_t ad_type; uint8_t sizeConsumed = 0; bool finished = false; setPayload(payload); + while(!finished) { length = *payload; // Retrieve the length of the record. payload++; // Skip to type - while (!finished) { sizeConsumed += 1 + length; // increase the size consumed. if (length != 0) { // A length of 0 indicates that we have reached the end. @@ -250,7 +250,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { ad_type, BLEUtils::advTypeToString(ad_type), length, pHex); free(pHex); - switch (ad_type) { + switch(ad_type) { case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09 setName(std::string(reinterpret_cast(payload), length)); break; @@ -273,7 +273,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { case ESP_BLE_AD_TYPE_16SRV_CMPL: case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 - for (int var = 0; var < length / 2; ++var) { + for (int var = 0; var < length/2; ++var) { setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 2))); } break; @@ -281,7 +281,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 - for (int var = 0; var < length / 4; ++var) { + for (int var = 0; var < length/4; ++var) { setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 4))); } break; @@ -308,7 +308,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); break; } - uint16_t uuid = *(uint16_t*) payload; + uint16_t uuid = *(uint16_t*)payload; setServiceDataUUID(BLEUUID(uuid)); if (length > 2) { setServiceData(std::string(reinterpret_cast(payload + 2), length - 2)); @@ -335,7 +335,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { break; } - setServiceDataUUID(BLEUUID(payload, (size_t) 16, false)); + setServiceDataUUID(BLEUUID(payload, (size_t)16, false)); if (length > 16) { setServiceData(std::string(reinterpret_cast(payload + 16), length - 16)); } @@ -351,7 +351,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { } // Length <> 0 - if (sizeConsumed >= 31 || length == 0) { + if (sizeConsumed >= total_len || length == 0) { finished = true; } } // !finished @@ -514,5 +514,14 @@ void BLEAdvertisedDevice::setPayload(uint8_t* payload) { m_payload = payload; } +esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() { + return m_addressType; +} + +void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) { + m_addressType = type; +} + #endif /* CONFIG_BT_ENABLED */ + diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index eebdd6ef..aaa1417c 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -40,6 +40,8 @@ class BLEAdvertisedDevice { BLEUUID getServiceUUID(); int8_t getTXPower(); uint8_t* getPayload(); + esp_ble_addr_type_t getAddressType(); + void setAddressType(esp_ble_addr_type_t type); bool isAdvertisingService(BLEUUID uuid); @@ -56,7 +58,7 @@ class BLEAdvertisedDevice { private: friend class BLEScan; - void parseAdvertisement(uint8_t* payload); + void parseAdvertisement(uint8_t* payload, size_t total_len=62); void setAddress(BLEAddress address); void setAdFlag(uint8_t adFlag); void setAdvertizementResult(uint8_t* payload); @@ -81,6 +83,7 @@ class BLEAdvertisedDevice { bool m_haveTXPower; + BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); uint8_t m_adFlag; uint16_t m_appearance; int m_deviceType; @@ -93,7 +96,7 @@ class BLEAdvertisedDevice { std::string m_serviceData; BLEUUID m_serviceDataUUID; uint8_t* m_payload; - BLEAddress m_address = BLEAddress((uint8_t*) "\0\0\0\0\0\0"); + esp_ble_addr_type_t m_addressType; }; /** From 68411ff57b9db1b80e4c9913c9ee619f0404f219 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 14 Nov 2018 19:49:30 +0100 Subject: [PATCH 286/310] Fix issue #618 --- cpp_utils/BLEUUID.cpp | 72 ++++++++++++++++++++++++------------------- cpp_utils/BLEUUID.h | 4 +-- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 512c24ea..ee8b6c9d 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -70,46 +70,56 @@ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { */ BLEUUID::BLEUUID(std::string value) { m_valueSet = true; - if (value.length() == 2) { + if (value.length() == 4) { m_uuid.len = ESP_UUID_LEN_16; - m_uuid.uuid.uuid16 = value[0] | (value[1] << 8); + m_uuid.uuid.uuid16 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid16 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4; + i+=2; + } } - else if (value.length() == 4) { + else if (value.length() == 8) { m_uuid.len = ESP_UUID_LEN_32; - m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + m_uuid.uuid.uuid32 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; + i+=2; + } + } + } } - else if (value.length() == 16) { + else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) m_uuid.len = ESP_UUID_LEN_128; - memrcpy(m_uuid.uuid.uuid128, (uint8_t*) value.data(), 16); + memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); } else if (value.length() == 36) { // If the length of the string is 36 bytes then we will assume it is a long hex string in // UUID format. m_uuid.len = ESP_UUID_LEN_128; - int vals[16]; - sscanf(value.c_str(), "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x", - &vals[15], - &vals[14], - &vals[13], - &vals[12], - &vals[11], - &vals[10], - &vals[9], - &vals[8], - &vals[7], - &vals[6], - &vals[5], - &vals[4], - &vals[3], - &vals[2], - &vals[1], - &vals[0] - ); - - for (int i = 0; i < 16; i++) { - m_uuid.uuid.uuid128[i] = vals[i]; + int n = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid128[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F); + i+=2; } - } else { + } + else { ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes"); m_valueSet = false; } @@ -249,7 +259,7 @@ BLEUUID BLEUUID::fromString(std::string _uuid) { } uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. - if (len == 4) { + if(len == 4) { uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); return BLEUUID(x); } else if (len == 8) { @@ -269,7 +279,7 @@ BLEUUID BLEUUID::fromString(std::string _uuid) { */ esp_bt_uuid_t* BLEUUID::getNative() { //ESP_LOGD(TAG, ">> getNative()") - if (!m_valueSet) { + if (m_valueSet == false) { ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!"); return nullptr; } diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index e81dd130..700739be 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -32,8 +32,8 @@ class BLEUUID { static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string private: - esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. - bool m_valueSet; // Is there a value set for this instance. + esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet = false; // Is there a value set for this instance. }; // BLEUUID #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ From 573644495bc718298d5d818a72ca3588a0834d7d Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 14 Nov 2018 23:09:24 +0100 Subject: [PATCH 287/310] Changes, bugfixes, new features. Add server and client multi connect feature. --- cpp_utils/BLECharacteristic.cpp | 188 ++++++++----------- cpp_utils/BLECharacteristic.h | 62 +++---- cpp_utils/BLEClient.cpp | 93 ++++++++-- cpp_utils/BLEClient.h | 22 ++- cpp_utils/BLEDevice.cpp | 248 +++++++++++++++++--------- cpp_utils/BLEDevice.h | 42 +++-- cpp_utils/BLERemoteCharacteristic.cpp | 33 ++-- cpp_utils/BLERemoteCharacteristic.h | 40 ++--- cpp_utils/BLERemoteService.cpp | 10 +- cpp_utils/BLERemoteService.h | 2 +- cpp_utils/BLEServer.cpp | 166 +++++++++-------- cpp_utils/BLEServer.h | 57 +++--- cpp_utils/BLEService.cpp | 28 ++- cpp_utils/BLEService.h | 15 +- cpp_utils/BLEUtils.cpp | 110 ++++++------ 15 files changed, 628 insertions(+), 488 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 6f696c7c..f3cf02b8 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -44,7 +44,7 @@ BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BL BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { m_bleUUID = uuid; m_handle = NULL_HANDLE; - m_properties = (esp_gatt_char_prop_t) 0; + m_properties = (esp_gatt_char_prop_t)0; m_pCallbacks = nullptr; setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); @@ -87,7 +87,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) { return; } - m_pService = pService; // Save the service for to which this characteristic belongs. + m_pService = pService; // Save the service to which this characteristic belongs. ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", getUUID().toString().c_str(), @@ -96,14 +96,6 @@ void BLECharacteristic::executeCreate(BLEService* pService) { esp_attr_control_t control; control.auto_rsp = ESP_GATT_RSP_BY_APP; - m_semaphoreCreateEvt.take("executeCreate"); - - /* - esp_attr_value_t value; - value.attr_len = m_value.getLength(); - value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; - value.attr_value = m_value.getData(); - */ esp_err_t errRc = ::esp_ble_gatts_add_char( m_pService->getHandle(), @@ -119,19 +111,6 @@ void BLECharacteristic::executeCreate(BLEService* pService) { return; } - m_semaphoreCreateEvt.wait("executeCreate"); - - // Now that we have registered the characteristic, we must also register all the descriptors associated with this - // characteristic. We iterate through each of those and invoke the registration call to register them with the - // ESP environment. - - BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); - - while (pDescriptor != nullptr) { - pDescriptor->executeCreate(this); - pDescriptor = m_descriptorMap.getNext(); - } // End while - ESP_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate @@ -216,7 +195,7 @@ void BLECharacteristic::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - switch (event) { + switch(event) { // Events handled: // // ESP_GATTS_ADD_CHAR_EVT @@ -246,7 +225,7 @@ void BLECharacteristic::handleGATTServerEvent( } else { m_value.cancel(); } - +// ??? esp_err_t errRc = ::esp_ble_gatts_send_response( gatts_if, param->write.conn_id, @@ -267,8 +246,15 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_ADD_CHAR_EVT: { if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && getHandle() == param->add_char.attr_handle && - getService()->getHandle() == param->add_char.service_handle) { - m_semaphoreCreateEvt.give(); + getService()->getHandle()==param->add_char.service_handle) { + + // we have created characteristic, now we can create descriptors + BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + while (pDescriptor != nullptr) { + pDescriptor->executeCreate(this); + pDescriptor = m_descriptorMap.getNext(); + } // End while + } break; } // ESP_GATTS_ADD_CHAR_EVT @@ -357,7 +343,7 @@ void BLECharacteristic::handleGATTServerEvent( // // If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. // If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than -// 22 bytes, then we "just" send it and that's the end of the story. +// 22 bytes, then we "just" send it and thats the end of the story. // If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. // If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. // Because of follow on request processing, we need to maintain an offset of how much data we have already sent @@ -367,10 +353,11 @@ void BLECharacteristic::handleGATTServerEvent( // // The following code has deliberately not been factored to make it fewer statements because this would cloud the // the logic flow comprehension. +// - // TODO requires some more research to confirm that 512 is max PDU like in bluetooth specs - uint16_t maxOffset = BLEDevice::getMTU() - 1; - if (BLEDevice::getMTU() > 512) maxOffset = 512; + // get mtu for peer device that we are sending read request to + uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1; + ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset); if (param->read.need_rsp) { ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); esp_gatt_rsp_t rsp; @@ -393,10 +380,6 @@ void BLECharacteristic::handleGATTServerEvent( } } else { // read.is_long == false - if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback - m_pCallbacks->onRead(this); // Invoke the read callback. - } - std::string value = m_value.getValue(); if (value.length() + 1 > maxOffset) { @@ -411,6 +394,10 @@ void BLECharacteristic::handleGATTServerEvent( rsp.attr_value.offset = 0; memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); } + + if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + m_pCallbacks->onRead(this); // Invoke the read callback. + } } rsp.attr_value.handle = param->read.handle; rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; @@ -440,12 +427,13 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t conn_id – The connection used. // case ESP_GATTS_CONF_EVT: { - m_semaphoreConfEvt.give(); + ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); + if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet + m_semaphoreConfEvt.give(param->conf.status); break; } case ESP_GATTS_CONNECT_EVT: { - m_semaphoreConfEvt.give(); break; } @@ -475,46 +463,9 @@ void BLECharacteristic::handleGATTServerEvent( * @return N/A */ void BLECharacteristic::indicate() { - ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); - - assert(getService() != nullptr); - assert(getService()->getServer() != nullptr); - - GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); - - if (getService()->getServer()->getConnectedCount() == 0) { - ESP_LOGD(LOG_TAG, "<< indicate: No connected clients."); - return; - } - - // Test to see if we have a 0x2902 descriptor. If we do, then check to see if indications are enabled - // and, if not, prevent the indication. - - BLE2902 *p2902 = (BLE2902*) getDescriptorByUUID((uint16_t) 0x2902); - if (p2902 != nullptr && !p2902->getIndications()) { - ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); - return; - } - - if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) { - ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum indicate size)", BLEDevice::getMTU() - 3); - } - - size_t length = m_value.getValue().length(); - - m_semaphoreConfEvt.take("indicate"); - - esp_err_t errRc = ::esp_ble_gatts_send_indicate( - getService()->getServer()->getGattsIf(), - getService()->getServer()->getConnId(), - getHandle(), length, (uint8_t*)m_value.getValue().data(), true); // The need_confirm = true makes this an indication. - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreConfEvt.wait("indicate"); + ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); + notify(false); ESP_LOGD(LOG_TAG, "<< indicate"); } // indicate @@ -525,7 +476,7 @@ void BLECharacteristic::indicate() { * will not block; it is a fire and forget. * @return N/A. */ -void BLECharacteristic::notify() { +void BLECharacteristic::notify(bool is_notification) { ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); assert(getService() != nullptr); @@ -541,31 +492,40 @@ void BLECharacteristic::notify() { // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled // and, if not, prevent the notification. - BLE2902 *p2902 = (BLE2902*) getDescriptorByUUID((uint16_t) 0x2902); - if (p2902 != nullptr && !p2902->getNotifications()) { - ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); - return; + BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(is_notification) { + if (p2902 != nullptr && !p2902->getNotifications()) { + ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); + return; + } } - - if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) { - ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", BLEDevice::getMTU() - 3); + else{ + if (p2902 != nullptr && !p2902->getIndications()) { + ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); + return; + } } + for (auto &myPair : getService()->getServer()->getPeerDevices(false)) { + uint16_t _mtu = (myPair.second.mtu); + if (m_value.getValue().length() > _mtu - 3) { + ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); + } - size_t length = m_value.getValue().length(); - - m_semaphoreConfEvt.take("notify"); - - esp_err_t errRc = ::esp_ble_gatts_send_indicate( - getService()->getServer()->getGattsIf(), - getService()->getServer()->getConnId(), - getHandle(), length, (uint8_t*)m_value.getValue().data(), false); // The need_confirm = false makes this a notify. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + size_t length = m_value.getValue().length(); + if(!is_notification) + m_semaphoreConfEvt.take("indicate"); + esp_err_t errRc = ::esp_ble_gatts_send_indicate( + getService()->getServer()->getGattsIf(), + myPair.first, + getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreConfEvt.give(); + return; + } + if(!is_notification) + m_semaphoreConfEvt.wait("indicate"); } - - m_semaphoreConfEvt.wait("notify"); - ESP_LOGD(LOG_TAG, "<< notify"); } // Notify @@ -580,9 +540,9 @@ void BLECharacteristic::notify() { void BLECharacteristic::setBroadcastProperty(bool value) { //ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); } } // setBroadcastProperty @@ -592,7 +552,7 @@ void BLECharacteristic::setBroadcastProperty(bool value) { * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. */ void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { - ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallbacks); + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks); m_pCallbacks = pCallbacks; ESP_LOGD(LOG_TAG, "<< setCallbacks"); } // setCallbacks @@ -622,9 +582,9 @@ void BLECharacteristic::setHandle(uint16_t handle) { void BLECharacteristic::setIndicateProperty(bool value) { //ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); } } // setIndicateProperty @@ -636,9 +596,9 @@ void BLECharacteristic::setIndicateProperty(bool value) { void BLECharacteristic::setNotifyProperty(bool value) { //ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); } } // setNotifyProperty @@ -650,9 +610,9 @@ void BLECharacteristic::setNotifyProperty(bool value) { void BLECharacteristic::setReadProperty(bool value) { //ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_READ); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); } } // setReadProperty @@ -683,7 +643,7 @@ void BLECharacteristic::setValue(uint8_t* data, size_t length) { * @return N/A. */ void BLECharacteristic::setValue(std::string value) { - setValue((uint8_t*) (value.data()), value.length()); + setValue((uint8_t*)(value.data()), value.length()); } // setValue void BLECharacteristic::setValue(uint16_t& data16) { @@ -713,13 +673,13 @@ void BLECharacteristic::setValue(int& data32) { void BLECharacteristic::setValue(float& data32) { uint8_t temp[4]; - *((float*) temp) = data32; + *((float*)temp) = data32; setValue(temp, 4); } // setValue void BLECharacteristic::setValue(double& data64) { uint8_t temp[8]; - *((double*) temp) = data64; + *((double*)temp) = data64; setValue(temp, 8); } // setValue @@ -731,9 +691,9 @@ void BLECharacteristic::setValue(double& data64) { void BLECharacteristic::setWriteNoResponseProperty(bool value) { //ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); } } // setWriteNoResponseProperty @@ -745,9 +705,9 @@ void BLECharacteristic::setWriteNoResponseProperty(bool value) { void BLECharacteristic::setWriteProperty(bool value) { //ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value); if (value) { - m_properties = (esp_gatt_char_prop_t) (m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); } else { - m_properties = (esp_gatt_char_prop_t) (m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); } } // setWriteProperty diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index bad5ba7a..49af0ba3 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -27,22 +27,19 @@ class BLECharacteristicCallbacks; */ class BLEDescriptorMap { public: - void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); - void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); + void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); + void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); BLEDescriptor* getByUUID(const char* uuid); BLEDescriptor* getByUUID(BLEUUID uuid); BLEDescriptor* getByHandle(uint16_t handle); std::string toString(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); BLEDescriptor* getFirst(); BLEDescriptor* getNext(); private: std::map m_uuidMap; - std::map m_handleMap; + std::map m_handleMap; std::map::iterator m_iterator; }; @@ -50,7 +47,7 @@ class BLEDescriptorMap { /** * @brief The model of a %BLE Characteristic. * - * A %BLE Characteristic is an identified value container that manages a value. It is exposed by a %BLE server and + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and * can be read and written to by a %BLE client. */ class BLECharacteristic { @@ -59,16 +56,15 @@ class BLECharacteristic { BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); virtual ~BLECharacteristic(); - void addDescriptor(BLEDescriptor* pDescriptor); + void addDescriptor(BLEDescriptor* pDescriptor); BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); - //size_t getLength(); - BLEUUID getUUID(); - std::string getValue(); - uint8_t* getData(); + BLEUUID getUUID(); + std::string getValue(); + uint8_t* getData(); void indicate(); - void notify(); + void notify(bool is_notification = true); void setBroadcastProperty(bool value); void setCallbacks(BLECharacteristicCallbacks* pCallbacks); void setIndicateProperty(bool value); @@ -80,19 +76,19 @@ class BLECharacteristic { void setValue(uint32_t& data32); void setValue(int& data32); void setValue(float& data32); - void setValue(double& data64); + void setValue(double& data64); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); std::string toString(); uint16_t getHandle(); void setAccessPermissions(esp_gatt_perm_t perm); - static const uint32_t PROPERTY_READ = 1 << 0; - static const uint32_t PROPERTY_WRITE = 1 << 1; - static const uint32_t PROPERTY_NOTIFY = 1 << 2; - static const uint32_t PROPERTY_BROADCAST = 1 << 3; - static const uint32_t PROPERTY_INDICATE = 1 << 4; - static const uint32_t PROPERTY_WRITE_NR = 1 << 5; + static const uint32_t PROPERTY_READ = 1<<0; + static const uint32_t PROPERTY_WRITE = 1<<1; + static const uint32_t PROPERTY_NOTIFY = 1<<2; + static const uint32_t PROPERTY_BROADCAST = 1<<3; + static const uint32_t PROPERTY_INDICATE = 1<<4; + static const uint32_t PROPERTY_WRITE_NR = 1<<5; private: @@ -101,24 +97,24 @@ class BLECharacteristic { friend class BLEDescriptor; friend class BLECharacteristicMap; - BLEUUID m_bleUUID; - BLEDescriptorMap m_descriptorMap; - uint16_t m_handle; - esp_gatt_char_prop_t m_properties; + BLEUUID m_bleUUID; + BLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + esp_gatt_char_prop_t m_properties; BLECharacteristicCallbacks* m_pCallbacks; - BLEService* m_pService; - BLEValue m_value; - esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + BLEService* m_pService; + BLEValue m_value; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - void executeCreate(BLEService* pService); + void executeCreate(BLEService* pService); esp_gatt_char_prop_t getProperties(); - BLEService* getService(); - void setHandle(uint16_t handle); + BLEService* getService(); + void setHandle(uint16_t handle); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); }; // BLECharacteristic diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 2a8d6f0c..4408b140 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -18,6 +18,7 @@ #include #include #include +#include "BLEDevice.h" #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif @@ -40,13 +41,14 @@ * We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics * and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors. * + * */ static const char* LOG_TAG = "BLEClient"; BLEClient::BLEClient() { m_pClientCallbacks = nullptr; - m_conn_id = 0; - m_gattc_if = 0; + m_conn_id = ESP_GATT_IF_NONE; + m_gattc_if = ESP_GATT_IF_NONE; m_haveServices = false; m_isConnected = false; // Initially, we are flagged as not connected. } // BLEClient @@ -80,22 +82,31 @@ void BLEClient::clearServices() { ESP_LOGD(LOG_TAG, "<< clearServices"); } // clearServices +/** + * Add overloaded function to ease connect to peer device with not public address + */ +bool BLEClient::connect(BLEAdvertisedDevice* device) { + BLEAddress address = device->getAddress(); + esp_ble_addr_type_t type = device->getAddressType(); + return connect(address, type); +} /** * @brief Connect to the partner (BLE Server). * @param [in] address The address of the partner. * @return True on success. */ -bool BLEClient::connect(BLEAddress address) { +bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); // We need the connection handle that we get from registering the application. We register the app // and then block on its completion. When the event has arrived, we will have the handle. + m_appId = BLEDevice::m_appId++; + BLEDevice::addPeerDevice(this, true, ESP_GATT_IF_NONE); m_semaphoreRegEvt.take("connect"); - clearServices(); // Delete any services that may exist. - - esp_err_t errRc = ::esp_ble_gattc_app_register(0); + // clearServices(); // we dont need to delete services since every client is unique? + esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; @@ -110,7 +121,7 @@ bool BLEClient::connect(BLEAddress address) { errRc = ::esp_ble_gattc_open( getGattcIf(), *getPeerAddress().getNative(), // address - BLE_ADDR_TYPE_PUBLIC, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. + type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. 1 // direct connection ); if (errRc != ESP_OK) { @@ -135,8 +146,6 @@ void BLEClient::disconnect() { ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } - esp_ble_gattc_app_unregister(getGattcIf()); - m_peerAddress = BLEAddress("00:00:00:00:00:00"); ESP_LOGD(LOG_TAG, "<< disconnect()"); } // disconnect @@ -149,8 +158,21 @@ void BLEClient::gattClientEventHandler( esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + // Execute handler code based on the type of event received. - switch (event) { + switch(event) { + + case ESP_GATTC_SRVC_CHG_EVT: + ESP_LOGI(LOG_TAG, "SERVICE CHANGED"); + break; + + case ESP_GATTC_CLOSE_EVT: { + // esp_ble_gattc_app_unregister(m_appId); + // BLEDevice::removePeerDevice(m_gattc_if, true); + break; + } // // ESP_GATTC_DISCONNECT_EVT @@ -178,7 +200,6 @@ void BLEClient::gattClientEventHandler( // - esp_gatt_status_t status // - uint16_t conn_id // - esp_bd_addr_t remote_bda - // - uint16_t mtu // case ESP_GATTC_OPEN_EVT: { m_conn_id = evtParam->open.conn_id; @@ -206,6 +227,26 @@ void BLEClient::gattClientEventHandler( break; } // ESP_GATTC_REG_EVT + case ESP_GATTC_CFG_MTU_EVT: + if(evtParam->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG,"Config mtu failed"); + } + m_mtu = evtParam->cfg_mtu.mtu; + break; + + case ESP_GATTC_CONNECT_EVT: { + BLEDevice::updatePeerDevice(this, true, m_gattc_if); + esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTC_CONNECT_EVT // // ESP_GATTC_SEARCH_CMPL_EVT @@ -215,6 +256,20 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // case ESP_GATTC_SEARCH_CMPL_EVT: { + esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam; + if (p_data->search_cmpl.status != ESP_GATT_OK){ + ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status); + break; + } +#ifndef ARDUINO_ARCH_ESP32 + if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) { + ESP_LOGI(LOG_TAG, "Get service information from remote device"); + } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) { + ESP_LOGI(LOG_TAG, "Get service information from flash"); + } else { + ESP_LOGI(LOG_TAG, "unknown service source"); + } +#endif m_semaphoreSearchCmplEvt.give(0); break; } // ESP_GATTC_SEARCH_CMPL_EVT @@ -238,6 +293,7 @@ void BLEClient::gattClientEventHandler( evtParam->search_res.end_handle ); m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); + m_servicesMapByInstID.insert(std::pair(pRemoteService, evtParam->search_res.srvc_id.inst_id)); break; } // ESP_GATTC_SEARCH_RES_EVT @@ -336,7 +392,7 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) { } } // End of each of the services. ESP_LOGD(LOG_TAG, "<< getService: not found"); - throw new BLEUuidNotFoundException; + return nullptr; } // getService @@ -355,20 +411,22 @@ std::map* BLEClient::getServices() { * and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received. */ ESP_LOGD(LOG_TAG, ">> getServices"); - +// TODO implement retrieving services from cache clearServices(); // Clear any services that may exist. - esp_err_t errRc = esp_ble_gattc_search_service( + ESP_LOGD(LOG_TAG, "esp_ble_gattc_get_service: %d services from peer device", count); + errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), - nullptr // Filter UUID + NULL // Filter UUID ); + m_semaphoreSearchCmplEvt.take("getServices"); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return &m_servicesMap; } - // If successful, remember that we now have services. + // If sucessfull, remember that we now have services. m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0); ESP_LOGD(LOG_TAG, "<< getServices"); return &m_servicesMap; @@ -450,6 +508,9 @@ void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::s ESP_LOGD(LOG_TAG, "<< setValue"); } // setValue +uint16_t BLEClient::getMTU() { + return m_mtu; +} /** * @brief Return a string representation of this client. diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index d66a8b4f..1b8144d5 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -19,9 +19,11 @@ #include "BLERemoteService.h" #include "BLEService.h" #include "BLEAddress.h" +#include "BLEAdvertisedDevice.h" class BLERemoteService; class BLEClientCallbacks; +class BLEAdvertisedDevice; /** * @brief A model of a %BLE client. @@ -31,7 +33,8 @@ class BLEClient { BLEClient(); ~BLEClient(); - bool connect(BLEAddress address); // Connect to the remote BLE Server + bool connect(BLEAdvertisedDevice* device); + bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server void disconnect(); // Disconnect from the remote BLE Server BLEAddress getPeerAddress(); // Get the address of the remote BLE Server int getRssi(); // Get the RSSI of the remote BLE Server @@ -47,12 +50,15 @@ class BLEClient { bool isConnected(); // Return true if we are connected. + void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. - void setClientCallbacks(BLEClientCallbacks* pClientCallbacks); std::string toString(); // Return a string representation of this client. + uint16_t getConnId(); + esp_gatt_if_t getGattcIf(); + uint16_t getMTU(); - +uint16_t m_appId; private: friend class BLEDevice; friend class BLERemoteService; @@ -64,14 +70,12 @@ class BLEClient { esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); - uint16_t getConnId(); - esp_gatt_if_t getGattcIf(); + BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. uint16_t m_conn_id; // int m_deviceType; - BLEAddress m_peerAddress = BLEAddress((uint8_t*) "\0\0\0\0\0\0"); // The BD address of the remote server. esp_gatt_if_t m_gattc_if; - bool m_haveServices; // Have we previously obtain the set of services from the remote server. - bool m_isConnected; // Are we currently connected. + bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. + bool m_isConnected = false; // Are we currently connected. BLEClientCallbacks* m_pClientCallbacks; FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); @@ -79,7 +83,9 @@ class BLEClient { FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); std::map m_servicesMap; + std::map m_servicesMapByInstID; void clearServices(); // Clear any existing services. + uint16_t m_mtu = 23; }; // class BLEDevice diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 77380d2b..add7e41e 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -41,11 +41,16 @@ static const char* LOG_TAG = "BLEDevice"; BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; BLEClient* BLEDevice::m_pClient = nullptr; -bool initialized = false; // Have we been initialized? -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t) 0; +bool initialized = false; +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; -uint16_t BLEDevice::m_localMTU = 23; +uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; +uint16_t BLEDevice::m_appId = 0; +std::map BLEDevice::m_connectedClientsMap; +gap_event_handler BLEDevice::m_customGapHandler = nullptr; +gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; +gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; /** * @brief Create a new instance of a client. @@ -74,7 +79,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; abort(); #endif // CONFIG_GATTS_ENABLE m_pServer = new BLEServer(); - m_pServer->createApp(0); + m_pServer->createApp(m_appId++); ESP_LOGD(LOG_TAG, "<< createServer"); return m_pServer; } // createServer @@ -100,20 +105,14 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; switch (event) { case ESP_GATTS_CONNECT_EVT: { - BLEDevice::m_localMTU = 23; #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { + if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } #endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT - case ESP_GATTS_MTU_EVT: { - BLEDevice::m_localMTU = param->mtu.mtu; - ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU); - break; - } default: { break; } @@ -123,6 +122,11 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; if (BLEDevice::m_pServer != nullptr) { BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); } + + if(m_customGattsHandler != nullptr) { + m_customGattsHandler(event, gatts_if, param); + } + } // gattServerEventHandler @@ -146,30 +150,29 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; switch(event) { case ESP_GATTC_CONNECT_EVT: { - if (BLEDevice::getMTU() != 23) { - esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { + if(BLEDevice::m_securityLevel){ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); } #endif // CONFIG_BLE_SMP_ENABLE break; - } // ESP_GATTC_CONNECT_EVT + } // ESP_GATTS_CONNECT_EVT default: break; } // switch + for(auto &myPair : BLEDevice::getPeerDevices(true)) { + conn_status_t conn_status = (conn_status_t)myPair.second; + if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){ + ((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param); + } + } - - // If we have a client registered, call it. - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); + if(m_customGattcHandler != nullptr) { + m_customGattcHandler(event, gattc_if, param); } + } // gattClientEventHandler @@ -178,33 +181,34 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; */ /* STATIC */ void BLEDevice::gapEventHandler( esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { + esp_ble_gap_cb_param_t *param) { BLEUtils::dumpGapEvent(event, param); - switch (event) { - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); - break; - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); - break; - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); - break; - case ESP_GAP_BLE_NC_REQ_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { - esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + switch(event) { + + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); } #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); } #endif // CONFIG_BLE_SMP_ENABLE @@ -212,14 +216,15 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; /* * TODO should we add white/black list comparison? */ - case ESP_GAP_BLE_SEC_REQ_EVT: - /* send the positive(true) security response to the peer device to accept the security request. - If not accept the security request, should sent the security response with negative(false) accept value*/ - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); - } else { + } + else{ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); } #endif // CONFIG_BLE_SMP_ENABLE @@ -227,39 +232,36 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; /* * */ - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. - // show the passkey number to the user to input it in the peer device. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { - ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + //display the passkey number to the user to input it in the peer deivce within 30 seconds + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + if(BLEDevice::m_securityCallbacks!=nullptr){ BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); } #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_KEY_EVT: - //shows the ble key type info share with peer device to the user. - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGD(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: - ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { - BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); - } + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } #endif // CONFIG_BLE_SMP_ENABLE - break; - default: + break; + default: { break; + } } // switch - if (BLEDevice::m_pServer != nullptr) { - BLEDevice::m_pServer->handleGAPEvent(event, param); - } - if (BLEDevice::m_pClient != nullptr) { BLEDevice::m_pClient->handleGAPEvent(event, param); } @@ -268,10 +270,13 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; BLEDevice::getScan()->handleGAPEvent(event, param); } - /* - * Security events: - */ + if(m_bleAdvertising != nullptr) { + BLEDevice::getAdvertising()->gapEventHandler(event, param); + } + if(m_customGapHandler != nullptr) { + BLEDevice::m_customGapHandler(event, param); + } } // gapEventHandler @@ -326,7 +331,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; * @param deviceName The device name of the device. */ /* STATIC */ void BLEDevice::init(std::string deviceName) { - if (!initialized) { + if(!initialized){ initialized = true; // Set the initialization flag to ensure we are only initialized once. esp_err_t errRc = ESP_OK; @@ -342,6 +347,9 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; return; } +#ifndef CLASSIC_BT_ENABLED + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); +#endif esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); errRc = esp_bt_controller_init(&bt_cfg); if (errRc != ESP_OK) { @@ -350,9 +358,7 @@ BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; } #ifndef CLASSIC_BT_ENABLED - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; @@ -544,12 +550,12 @@ bool BLEDevice::getInitialized() { } BLEAdvertising* BLEDevice::getAdvertising() { - if (m_bleAdvertising == nullptr) { + if(m_bleAdvertising == nullptr) { m_bleAdvertising = new BLEAdvertising(); ESP_LOGI(LOG_TAG, "create advertising"); } ESP_LOGD(LOG_TAG, "get advertising"); - return m_bleAdvertising; + return m_bleAdvertising; } void BLEDevice::startAdvertising() { @@ -558,4 +564,80 @@ void BLEDevice::startAdvertising() { ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising +/* multi connect support */ +/* requires a little more work */ +std::map BLEDevice::getPeerDevices(bool _client) { + return m_connectedClientsMap; +} + +BLEClient* BLEDevice::getClientByGattIf(uint16_t conn_id) { + return (BLEClient*)m_connectedClientsMap.find(conn_id)->second.peer_device; +} + +void BLEDevice::updatePeerDevice(void* peer, bool _client, uint16_t conn_id) { + ESP_LOGD(LOG_TAG, "update conn_id: %d, GATT role: %s", conn_id, _client? "client":"server"); + std::map::iterator it = m_connectedClientsMap.find(ESP_GATT_IF_NONE); + if (it != m_connectedClientsMap.end()) { + std::swap(m_connectedClientsMap[conn_id], it->second); + m_connectedClientsMap.erase(it); + }else{ + it = m_connectedClientsMap.find(conn_id); + if (it != m_connectedClientsMap.end()) { + conn_status_t _st = it->second; + _st.peer_device = peer; + std::swap(m_connectedClientsMap[conn_id], _st); + } + } +} + +void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + ESP_LOGI(LOG_TAG, "add conn_id: %d, GATT role: %s", conn_id, _client? "client":"server"); + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedClientsMap.insert(std::pair(conn_id, status)); +} + +void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) { + ESP_LOGI(LOG_TAG, "remove: %d, GATT role %s", conn_id, _client?"client":"server"); + m_connectedClientsMap.erase(conn_id); +} + +/* multi connect support */ + +/** + * @brief de-Initialize the %BLE environment. + * @param release_memory release the internal BT stack memory + */ +/* STATIC */ void BLEDevice::deinit(bool release_memory) { + if (!initialized) return; + + esp_bluedroid_disable(); + esp_bluedroid_deinit(); + esp_bt_controller_disable(); + esp_bt_controller_deinit(); +#ifndef ARDUINO_ARCH_ESP32 + if (release_memory) { + esp_bt_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it) + } else { + initialized = false; + } +#endif +} + +void BLEDevice::setCustomGapHandler(gap_event_handler handler) { + m_customGapHandler = handler; +} + +void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) { + m_customGattcHandler = handler; +} + +void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { + m_customGattsHandler = handler; +} + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 9fd53ebb..e9cd40a3 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -22,8 +22,12 @@ #include "BLEAddress.h" /** - * @brief %BLE functions. + * @brief BLE functions. */ +typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); +typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); +typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param); + class BLEDevice { public: @@ -43,19 +47,31 @@ class BLEDevice { static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); static bool getInitialized(); // Returns the state of the device, is it initialized or not? - static BLEAdvertising* getAdvertising(); - static void startAdvertising(); + /* move advertising to BLEDevice for saving ram and flash in beacons */ + static BLEAdvertising* getAdvertising(); + static void startAdvertising(); + static uint16_t m_appId; + /* multi connect */ + static std::map getPeerDevices(bool client); + static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id); + static void removePeerDevice(uint16_t conn_id, bool client); + static BLEClient* getClientByGattIf(uint16_t conn_id); + static void setCustomGapHandler(gap_event_handler handler); + static void setCustomGattcHandler(gattc_event_handler handler); + static void setCustomGattsHandler(gatts_event_handler handler); + static void deinit(bool release_memory = false); + static uint16_t m_localMTU; + static esp_ble_sec_act_t m_securityLevel; private: - static BLEServer* m_pServer; - static BLEScan* m_pScan; - static BLEClient* m_pClient; - static esp_ble_sec_act_t m_securityLevel; + static BLEServer* m_pServer; + static BLEScan* m_pScan; + static BLEClient* m_pClient; static BLESecurityCallbacks* m_securityCallbacks; - static uint16_t m_localMTU; - static BLEAdvertising* m_bleAdvertising; - + static BLEAdvertising* m_bleAdvertising; static esp_gatt_if_t getGattcIF(); + static std::map m_connectedClientsMap; static void gattClientEventHandler( esp_gattc_cb_event_t event, @@ -71,6 +87,12 @@ class BLEDevice { esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); +public: +/* custom gap and gatt handlers for flexibility */ + static gap_event_handler m_customGapHandler; + static gattc_event_handler m_customGattcHandler; + static gatts_event_handler m_customGattsHandler; + }; // class BLE #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 14177eac..1768dbb6 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -147,7 +147,7 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { * @returns N/A */ void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { - switch (event) { + switch(event) { // ESP_GATTC_NOTIFY_EVT // // notify @@ -187,8 +187,7 @@ void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, // and unlock the semaphore to ensure that the requestor of the data can continue. if (evtParam->read.status == ESP_GATT_OK) { m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len); - if (m_rawData != nullptr) free(m_rawData); - + if(m_rawData != nullptr) free(m_rawData); m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); } else { @@ -260,8 +259,8 @@ void BLERemoteCharacteristic::retrieveDescriptors() { // For each descriptor we find, create a BLERemoteDescriptor instance. uint16_t offset = 0; esp_gattc_descr_elem_t result; - while (true) { - uint16_t count = 1; + while(true) { + uint16_t count = 10; esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( getRemoteService()->getClient()->getGattcIf(), getRemoteService()->getClient()->getConnId(), @@ -281,7 +280,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { } if (count == 0) break; - ESP_LOGE(LOG_TAG, ""); + ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); // We now have a new characteristic ... let us add that to our set of known characteristics @@ -327,7 +326,7 @@ uint16_t BLERemoteCharacteristic::getHandle() { BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); std::string v = uuid.toString(); - for (auto& myPair : m_descriptorMap) { + for (auto &myPair : m_descriptorMap) { if (myPair.first == v) { ESP_LOGD(LOG_TAG, "<< getDescriptor: found"); return myPair.second; @@ -363,7 +362,7 @@ BLEUUID BLERemoteCharacteristic::getUUID() { uint16_t BLERemoteCharacteristic::readUInt16() { std::string value = readValue(); if (value.length() >= 2) { - return *(uint16_t*) value.data(); + return *(uint16_t*)(value.data()); } return 0; } // readUInt16 @@ -376,7 +375,7 @@ uint16_t BLERemoteCharacteristic::readUInt16() { uint32_t BLERemoteCharacteristic::readUInt32() { std::string value = readValue(); if (value.length() >= 4) { - return *(uint32_t*) value.data(); + return *(uint32_t*)(value.data()); } return 0; } // readUInt32 @@ -389,7 +388,7 @@ uint32_t BLERemoteCharacteristic::readUInt32() { uint8_t BLERemoteCharacteristic::readUInt8() { std::string value = readValue(); if (value.length() >= 1) { - return (uint8_t) value[0]; + return (uint8_t)value[0]; } return 0; } // readUInt8 @@ -439,12 +438,7 @@ std::string BLERemoteCharacteristic::readValue() { * unregistering a notification. * @return N/A. */ -void BLERemoteCharacteristic::registerForNotify( - void (*notifyCallback)( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify), bool notifications) { +void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications) { ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); m_notifyCallback = notifyCallback; // Save the notification callback. @@ -463,8 +457,8 @@ void BLERemoteCharacteristic::registerForNotify( } uint8_t val[] = {0x01, 0x00}; - if (!notifications) val[0] = 0x02; - BLERemoteDescriptor* desc = getDescriptor(BLEUUID("0x2902")); + if(!notifications) val[0] = 0x02; + BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902)); desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. @@ -535,7 +529,6 @@ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { } m_semaphoreWriteCharEvt.take("writeValue"); - // Invoke the ESP-IDF API to perform the write. esp_err_t errRc = ::esp_ble_gattc_write_char( m_pRemoteService->getClient()->getGattcIf(), @@ -578,7 +571,7 @@ void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { * @param [in] response Whether we require a response from the write. */ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { - writeValue(std::string((char*) data, length), response); + writeValue(std::string((char*)data, length), response); } // writeValue /** diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 0750c1a9..fbcafe8d 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -21,6 +21,7 @@ class BLERemoteService; class BLERemoteDescriptor; +typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); /** * @brief A model of a remote %BLE characteristic. @@ -37,18 +38,18 @@ class BLERemoteCharacteristic { bool canWrite(); bool canWriteNoResponse(); BLERemoteDescriptor* getDescriptor(BLEUUID uuid); + std::map* getDescriptors(); uint16_t getHandle(); BLEUUID getUUID(); - std::map* getDescriptors(); - std::string readValue(void); - uint8_t readUInt8(void); - uint16_t readUInt16(void); - uint32_t readUInt32(void); - void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify), bool notifications = true); + std::string readValue(); + uint8_t readUInt8(); + uint16_t readUInt16(); + uint32_t readUInt32(); + void registerForNotify(notify_callback _callback, bool notifications = true); void writeValue(uint8_t* data, size_t length, bool response = false); void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); - std::string toString(void); + std::string toString(); uint8_t* readRawData(); private: @@ -58,26 +59,23 @@ class BLERemoteCharacteristic { friend class BLERemoteDescriptor; // Private member functions - void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* evtParam); + void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); BLERemoteService* getRemoteService(); - void removeDescriptors(); - void retrieveDescriptors(); + void removeDescriptors(); + void retrieveDescriptors(); // Private properties - BLEUUID m_uuid; + BLEUUID m_uuid; esp_gatt_char_prop_t m_charProp; - uint16_t m_handle; - BLERemoteService* m_pRemoteService; - FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); + uint16_t m_handle; + BLERemoteService* m_pRemoteService; + FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); - FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); - std::string m_value; - uint8_t* m_rawData; - void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); + std::string m_value; + uint8_t *m_rawData; + notify_callback m_notifyCallback; // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. std::map m_descriptorMap; diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index d632cff5..ef349986 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -126,10 +126,9 @@ void BLERemoteService::gattClientEventHandler( * @throws BLEUuidNotFoundException */ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { - return getCharacteristic(BLEUUID(uuid)); + return getCharacteristic(BLEUUID(uuid)); } // getCharacteristic - - + /** * @brief Get the characteristic object for the UUID. * @param [in] uuid Characteristic uuid. @@ -152,7 +151,8 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { return myPair.second; } } - throw new BLEUuidNotFoundException(); + // throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another + return nullptr; } // getCharacteristic @@ -169,7 +169,7 @@ void BLERemoteService::retrieveCharacteristics() { uint16_t offset = 0; esp_gattc_char_elem_t result; while (true) { - uint16_t count = 1; + uint16_t count = 10; // this value is used as in parameter that allows to search max 10 chars with the same uuid esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( getClient()->getGattcIf(), getClient()->getConnId(), diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 387afbdf..0f45bd0a 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -33,7 +33,7 @@ class BLERemoteService { BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. std::map* getCharacteristics(); - void getCharacteristics(std::map* pCharacteristicMap); // Get the characteristics map. + std::map* getCharacteristicsByHandle(); // Get the characteristics map. BLEClient* getClient(void); // Get a reference to the client associated with this service. uint16_t getHandle(); // Get the handle of this service. diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index e5c675b8..63962027 100755 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -10,8 +10,7 @@ #include #include #include -#include -//#include +#include "GeneralUtils.h" #include "BLEDevice.h" #include "BLEServer.h" #include "BLEService.h" @@ -33,18 +32,17 @@ static const char* LOG_TAG = "BLEServer"; * the BLEDevice class. */ BLEServer::BLEServer() { - m_appId = -1; - m_gatts_if = -1; + m_appId = ESP_GATT_IF_NONE; + m_gatts_if = ESP_GATT_IF_NONE; m_connectedCount = 0; - m_connId = -1; + m_connId = ESP_GATT_IF_NONE; m_pServerCallbacks = nullptr; - //createApp(0); } // BLEServer void BLEServer::createApp(uint16_t appId) { m_appId = appId; - registerApp(); + registerApp(appId); } // createApp @@ -79,12 +77,10 @@ BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t if (m_serviceMap.getByUUID(uuid) != nullptr) { ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); - //m_semaphoreCreateEvt.give(); - //return nullptr; } BLEService* pService = new BLEService(uuid, numHandles); - pService->m_id = inst_id; + pService->m_instId = inst_id; m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. @@ -104,7 +100,6 @@ BLEService* BLEServer::getServiceByUUID(const char* uuid) { return m_serviceMap.getByUUID(uuid); } - /** * @brief Get a %BLE Service by its UUID * @param [in] uuid The UUID of the new service. @@ -114,20 +109,6 @@ BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { return m_serviceMap.getByUUID(uuid); } - -/** - * @brief Returns the amount of services registered to this server - * @param [in] includeDefaultServices Add the amount of default BluetoothLE services defined by the BLE standard - * @return The amount of registered services - */ -int BLEServer::getServiceCount(bool includeDefaultServices) { - if (includeDefaultServices) { - return m_serviceMap.getRegisteredServiceCount() + 2; - } - return m_serviceMap.getRegisteredServiceCount(); -} - - /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. * @@ -137,7 +118,6 @@ BLEAdvertising* BLEServer::getAdvertising() { return BLEDevice::getAdvertising(); } - uint16_t BLEServer::getConnId() { return m_connId; } @@ -156,41 +136,6 @@ uint16_t BLEServer::getGattsIf() { return m_gatts_if; } -/** - * @brief Handle a received GAP event. - * - * @param [in] event - * @param [in] param - */ -void BLEServer::handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { - ESP_LOGD(LOG_TAG, "BLEServer ... handling GAP event!"); - switch (event) { - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - /* - esp_ble_adv_params_t adv_params; - adv_params.adv_int_min = 0x20; - adv_params.adv_int_max = 0x40; - adv_params.adv_type = ADV_TYPE_IND; - adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - adv_params.channel_map = ADV_CHNL_ALL; - adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; - ESP_LOGD(tag, "Starting advertising"); - esp_err_t errRc = ::esp_ble_gap_start_advertising(&adv_params); - if (errRc != ESP_OK) { - ESP_LOGE(tag, "esp_ble_gap_start_advertising: rc=%d %s", errRc, espToString(errRc)); - return; - } - */ - break; - } - - default: - break; - } -} // handleGAPEvent - /** * @brief Handle a GATT Server Event. @@ -201,12 +146,10 @@ void BLEServer::handleGAPEvent( * */ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { - ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - - // Invoke the handler for every Service we have. - m_serviceMap.handleGATTServerEvent(event, gatts_if, param); + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", + BLEUtils::gattServerEventTypeToString(event).c_str()); - switch (event) { + switch(event) { // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status @@ -218,20 +161,23 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t break; } // ESP_GATTS_ADD_CHAR_EVT + case ESP_GATTS_MTU_EVT: + updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); + break; // ESP_GATTS_CONNECT_EVT // connect: // - uint16_t conn_id // - esp_bd_addr_t remote_bda - // - bool is_connected // case ESP_GATTS_CONNECT_EVT: { - m_connId = param->connect.conn_id; // Save the connection id. + m_connId = param->connect.conn_id; + addPeerDevice((void*)this, false, m_connId); if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); - m_pServerCallbacks->onConnect(this, param); + m_pServerCallbacks->onConnect(this, param); } - m_connectedCount++; // Increment the number of connected devices count. + m_connectedCount++; // Increment the number of connected devices count. break; } // ESP_GATTS_CONNECT_EVT @@ -245,7 +191,7 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t // * esp_gatt_srvc_id_t service_id // case ESP_GATTS_CREATE_EVT: { - BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid); + BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid m_serviceMap.setByHandle(param->create.service_handle, pService); m_semaphoreCreateEvt.give(); break; @@ -255,9 +201,9 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t // ESP_GATTS_DISCONNECT_EVT // // disconnect - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda - // - bool is_connected + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason // // If we receive a disconnect event then invoke the callback for disconnects (if one is present). // we also want to start advertising again. @@ -267,6 +213,7 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t m_pServerCallbacks->onDisconnect(this); } startAdvertising(); //- do this with some delay from the loop() + removePeerDevice(param->disconnect.conn_id, false); break; } // ESP_GATTS_DISCONNECT_EVT @@ -316,9 +263,17 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t break; } + case ESP_GATTS_OPEN_EVT: + m_semaphoreOpenEvt.give(param->open.status); + break; + default: break; } + + // Invoke the handler for every Service we have. + m_serviceMap.handleGATTServerEvent(event, gatts_if, param); + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); } // handleGATTServerEvent @@ -328,7 +283,7 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t * * @return N/A */ -void BLEServer::registerApp() { +void BLEServer::registerApp(uint16_t m_appId) { ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. ::esp_ble_gatts_app_register(m_appId); @@ -355,7 +310,7 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { */ void BLEServer::removeService(BLEService* service) { service->stop(); - service->executeDelete(); + service->executeDelete(); m_serviceMap.removeService(service); } @@ -371,6 +326,31 @@ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising +/** + * Allow to connect GATT server to peer device + * Probably can be used in ANCS for iPhone + */ +bool BLEServer::connect(BLEAddress address) { + esp_bd_addr_t addr; + memcpy(&addr, address.getNative(), 6); + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + esp_err_t errRc = ::esp_ble_gatts_open( + getGattsIf(), + addr, // address + 1 // direct connection + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); + return rc == ESP_GATT_OK; +} // connect + + void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); @@ -391,4 +371,38 @@ void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect +/* multi connect support */ +/* TODO do some more tweaks */ +void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { + // set mtu in conn_status_t + const std::map::iterator it = m_connectedServersMap.find(conn_id); + if (it != m_connectedServersMap.end()) { + it->second.mtu = mtu; + std::swap(m_connectedServersMap[conn_id], it->second); + } +} + +std::map BLEServer::getPeerDevices(bool _client) { + return m_connectedServersMap; +} + + +uint16_t BLEServer::getPeerMTU(uint16_t conn_id) { + return m_connectedServersMap.find(conn_id)->second.mtu; +} + +void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedServersMap.insert(std::pair(conn_id, status)); +} + +void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { + m_connectedServersMap.erase(conn_id); +} +/* multi connect support */ #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 799eb25d..1dbcab65 100755 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -13,6 +13,7 @@ #include #include +// #include "BLEDevice.h" #include "BLEUUID.h" #include "BLEAdvertising.h" @@ -20,8 +21,15 @@ #include "BLEService.h" #include "BLESecurity.h" #include "FreeRTOS.h" +#include "BLEAddress.h" class BLEServerCallbacks; +/* TODO possibly refactor this struct */ +typedef struct { + void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here + bool connected; // do we need it? + uint16_t mtu; // every peer device negotiate own mtu +} conn_status_t; /** @@ -31,26 +39,20 @@ class BLEServiceMap { public: BLEService* getByHandle(uint16_t handle); BLEService* getByUUID(const char* uuid); - BLEService* getByUUID(BLEUUID uuid); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); + BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setByHandle(uint16_t handle, BLEService* service); void setByUUID(const char* uuid, BLEService* service); void setByUUID(BLEUUID uuid, BLEService* service); std::string toString(); BLEService* getFirst(); BLEService* getNext(); - void removeService(BLEService* service); - int getRegisteredServiceCount(); - + void removeService(BLEService *service); private: std::map m_handleMap; std::map m_uuidMap; std::map::iterator m_iterator; - }; @@ -61,14 +63,24 @@ class BLEServer { public: uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid, uint32_t numHandles = 15, uint8_t inst_id = 0); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); + void removeService(BLEService* service); BLEService* getServiceByUUID(const char* uuid); BLEService* getServiceByUUID(BLEUUID uuid); - void removeService(BLEService* service); - int getServiceCount(bool includeDefaultServices); + bool connect(BLEAddress address); + uint16_t m_appId; + + /* multi connection support */ + std::map getPeerDevices(bool client); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); + BLEServer* getServerByConnId(uint16_t conn_id); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + private: BLEServer(); @@ -76,22 +88,23 @@ class BLEServer { friend class BLECharacteristic; friend class BLEDevice; esp_ble_adv_data_t m_adv_data; - uint16_t m_appId; // BLEAdvertising m_bleAdvertising; - uint16_t m_connId; - uint32_t m_connectedCount; - uint16_t m_gatts_if; - FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + uint16_t m_connId; + uint32_t m_connectedCount; + uint16_t m_gatts_if; + std::map m_connectedServersMap; + + FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); BLEServiceMap m_serviceMap; - BLEServerCallbacks* m_pServerCallbacks; + BLEServerCallbacks* m_pServerCallbacks = nullptr; void createApp(uint16_t appId); uint16_t getConnId(); uint16_t getGattsIf(); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void registerApp(); - void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + void registerApp(uint16_t); }; // BLEServer @@ -109,7 +122,7 @@ class BLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the client connection. */ virtual void onConnect(BLEServer* pServer); - virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param); + virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); /** * @brief Handle an existing client disconnection. * diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 68019608..9e8b05ae 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -62,18 +62,13 @@ BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) { */ void BLEService::executeCreate(BLEServer* pServer) { - //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - //getUUID(); // Needed for a weird bug fix - //char x[1000]; - //memcpy(x, &m_uuid, sizeof(m_uuid)); - //char x[10]; - //memcpy(x, &deleteMe, 10); + ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); m_pServer = pServer; m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT esp_gatt_srvc_id_t srvc_id; srvc_id.is_primary = true; - srvc_id.id.inst_id = m_id; + srvc_id.id.inst_id = m_instId; srvc_id.id.uuid = *m_uuid.getNative(); esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. @@ -137,10 +132,10 @@ BLEUUID BLEService::getUUID() { * @return Start the service. */ void BLEService::start() { - // We ask the BLE runtime to start the service and then create each of the characteristics. - // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event - // obtained as a result of calling esp_ble_gatts_create_service(). - // +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); @@ -174,9 +169,9 @@ void BLEService::start() { * @brief Stop the service. */ void BLEService::stop() { - // We ask the BLE runtime to start the service and then create each of the characteristics. - // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event - // obtained as a result of calling esp_ble_gatts_create_service(). +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); @@ -228,6 +223,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // We maintain a mapping of characteristics owned by this service. These are managed by the // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), @@ -256,7 +252,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { return createCharacteristic(BLEUUID(uuid), properties); } - + /** * @brief Create a new BLE Characteristic associated with this service. @@ -342,7 +338,7 @@ void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t // * - bool is_primary // case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) { setHandle(param->create.service_handle); m_semaphoreCreateEvt.give(); } diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index 711ac707..b42d57f2 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -27,7 +27,7 @@ class BLECharacteristicMap { void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(const char* uuid); BLECharacteristic* getByUUID(BLEUUID uuid); BLECharacteristic* getByHandle(uint16_t handle); BLECharacteristic* getFirst(); @@ -53,16 +53,16 @@ class BLEService { BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); void dump(); void executeCreate(BLEServer* pServer); - void executeDelete(); + void executeDelete(); BLECharacteristic* getCharacteristic(const char* uuid); BLECharacteristic* getCharacteristic(BLEUUID uuid); BLEUUID getUUID(); BLEServer* getServer(); void start(); - void stop(); + void stop(); std::string toString(); uint16_t getHandle(); - uint8_t m_id = 0; + uint8_t m_instId = 0; private: BLEService(const char* uuid, uint16_t numHandles); @@ -75,8 +75,8 @@ class BLEService { BLECharacteristicMap m_characteristicMap; uint16_t m_handle; - BLECharacteristic* m_lastCreatedCharacteristic; - BLEServer* m_pServer; + BLECharacteristic* m_lastCreatedCharacteristic = nullptr; + BLEServer* m_pServer = nullptr; BLEUUID m_uuid; FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); @@ -87,10 +87,9 @@ class BLEService { uint16_t m_numHandles; BLECharacteristic* getLastCreatedCharacteristic(); - void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setHandle(uint16_t handle); //void setService(esp_gatt_srvc_id_t srvc_id); - }; // BLEService diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 0dd5f461..d6384931 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -730,7 +730,7 @@ const char* BLEUtils::advTypeToString(uint8_t advType) { case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; default: - ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType); + ESP_LOGV(LOG_TAG, " adv data type: 0x%x", advType); return ""; } // End switch } // advTypeToString @@ -1034,13 +1034,13 @@ const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) { void BLEUtils::dumpGapEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { - ESP_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event)); + ESP_LOGV(LOG_TAG, "Received a GAP event: %s", gapEventToString(event)); switch (event) { // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT // adv_data_cmpl // - esp_bt_status_t case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT @@ -1049,7 +1049,7 @@ void BLEUtils::dumpGapEvent( // adv_data_raw_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT @@ -1058,7 +1058,7 @@ void BLEUtils::dumpGapEvent( // adv_start_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); break; } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT @@ -1067,7 +1067,7 @@ void BLEUtils::dumpGapEvent( // adv_stop_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT @@ -1082,7 +1082,7 @@ void BLEUtils::dumpGapEvent( // - esp_bd_addr_type_t addr_type // - esp_bt_dev_type_t dev_type case ESP_GAP_BLE_AUTH_CMPL_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", + ESP_LOGV(LOG_TAG, "[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, @@ -1098,7 +1098,7 @@ void BLEUtils::dumpGapEvent( // clear_bond_dev_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); break; } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT @@ -1114,7 +1114,7 @@ void BLEUtils::dumpGapEvent( // ESP_GAP_BLE_NC_REQ_EVT case ESP_GAP_BLE_NC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s, passkey: %d]", + ESP_LOGV(LOG_TAG, "[bd_addr: %s, passkey: %d]", BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(), param->ble_security.key_notif.passkey); break; @@ -1127,7 +1127,7 @@ void BLEUtils::dumpGapEvent( // - int8_t rssi // - esp_bd_addr_t remote_addr case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", + ESP_LOGV(LOG_TAG, "[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() @@ -1140,7 +1140,7 @@ void BLEUtils::dumpGapEvent( // scan_param_cmpl. // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT @@ -1161,7 +1161,7 @@ void BLEUtils::dumpGapEvent( case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch (param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { - ESP_LOGD(LOG_TAG, "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", + ESP_LOGV(LOG_TAG, "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), @@ -1178,7 +1178,7 @@ void BLEUtils::dumpGapEvent( } // ESP_GAP_SEARCH_INQ_RES_EVT default: { - ESP_LOGD(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); + ESP_LOGV(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); break; } } @@ -1190,13 +1190,13 @@ void BLEUtils::dumpGapEvent( // scan_rsp_data_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + ESP_LOGV(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); break; } // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT @@ -1205,7 +1205,7 @@ void BLEUtils::dumpGapEvent( // scan_start_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); break; } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT @@ -1214,7 +1214,7 @@ void BLEUtils::dumpGapEvent( // scan_stop_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); break; } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT @@ -1229,7 +1229,7 @@ void BLEUtils::dumpGapEvent( // - uint16_t conn_int // - uint16_t timeout case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", + ESP_LOGV(LOG_TAG, "[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, @@ -1243,12 +1243,12 @@ void BLEUtils::dumpGapEvent( // ESP_GAP_BLE_SEC_REQ_EVT case ESP_GAP_BLE_SEC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + ESP_LOGV(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); break; } // ESP_GAP_BLE_SEC_REQ_EVT default: { - ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); + ESP_LOGV(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; } // default } // switch @@ -1267,7 +1267,7 @@ void BLEUtils::dumpGattClientEvent( esp_ble_gattc_cb_param_t* evtParam) { //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; - ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); + ESP_LOGV(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); switch (event) { // ESP_GATTC_CLOSE_EVT // @@ -1277,7 +1277,7 @@ void BLEUtils::dumpGattClientEvent( // - esp_bd_addr_t remote_bda // - esp_gatt_conn_reason_t reason case ESP_GATTC_CLOSE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]", + ESP_LOGV(LOG_TAG, "[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); @@ -1291,7 +1291,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str() ); @@ -1305,7 +1305,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[reason: %s, conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[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() @@ -1331,7 +1331,7 @@ void BLEUtils::dumpGattClientEvent( if (evtParam->get_char.char_id.uuid.len == ESP_UUID_LEN_16) { description = BLEUtils::gattCharacteristicUUIDToString(evtParam->get_char.char_id.uuid.uuid.uuid16); } - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]", + ESP_LOGV(LOG_TAG, "[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(), @@ -1340,7 +1340,7 @@ void BLEUtils::dumpGattClientEvent( BLEUtils::characteristicPropertiesToString(evtParam->get_char.char_prop).c_str() ); } else { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s]", + ESP_LOGV(LOG_TAG, "[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() @@ -1361,7 +1361,7 @@ void BLEUtils::dumpGattClientEvent( // bool is_notify // case ESP_GATTC_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", + ESP_LOGV(LOG_TAG, "[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, @@ -1381,7 +1381,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t mtu // case ESP_GATTC_OPEN_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]", + ESP_LOGV(LOG_TAG, "[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(), @@ -1401,7 +1401,7 @@ void BLEUtils::dumpGattClientEvent( // uint16_t value_type // uint16_t value_len case ESP_GATTC_READ_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", + ESP_LOGV(LOG_TAG, "[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, @@ -1412,7 +1412,7 @@ void BLEUtils::dumpGattClientEvent( GeneralUtils::hexDump(evtParam->read.value, evtParam->read.value_len); /* char* pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); - ESP_LOGD(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); + ESP_LOGV(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); free(pHexData); */ } @@ -1425,7 +1425,7 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_status_t status // - uint16_t app_id case ESP_GATTC_REG_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, app_id: 0x%x]", + ESP_LOGV(LOG_TAG, "[status: %s, app_id: 0x%x]", BLEUtils::gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; @@ -1437,7 +1437,7 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_status_t status // - uint16_t handle case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, handle: %d 0x%.2x]", + ESP_LOGV(LOG_TAG, "[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 @@ -1451,7 +1451,7 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_status_t status // - uint16_t conn_id case ESP_GATTC_SEARCH_CMPL_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(), evtParam->search_cmpl.conn_id); break; @@ -1465,7 +1465,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t end_handle // - esp_gatt_id_t srvc_id case ESP_GATTC_SEARCH_RES_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", + ESP_LOGV(LOG_TAG, "[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, @@ -1483,7 +1483,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t handle // - uint16_t offset case ESP_GATTC_WRITE_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]", + ESP_LOGV(LOG_TAG, "[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, @@ -1513,11 +1513,11 @@ void BLEUtils::dumpGattServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam) { - ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + ESP_LOGV(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); switch (event) { case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + ESP_LOGV(LOG_TAG, "[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, @@ -1529,7 +1529,7 @@ void BLEUtils::dumpGattServerEvent( case ESP_GATTS_ADD_CHAR_EVT: { if (evtParam->add_char.status == ESP_GATT_OK) { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + ESP_LOGV(LOG_TAG, "[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, @@ -1555,7 +1555,7 @@ void BLEUtils::dumpGattServerEvent( // - esp_gatt_status_t status – The status code. // - uint16_t conn_id – The connection used. case ESP_GATTS_CONF_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: 0x%.2x]", gattStatusToString(evtParam->conf.status).c_str(), evtParam->conf.conn_id); break; @@ -1563,21 +1563,21 @@ void BLEUtils::dumpGattServerEvent( case ESP_GATTS_CONGEST_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, congested: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, congested: %d]", evtParam->congest.conn_id, evtParam->congest.congested); break; } // ESP_GATTS_CONGEST_EVT case ESP_GATTS_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[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: { - ESP_LOGD(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", gattStatusToString(evtParam->create.status).c_str(), evtParam->create.service_handle, evtParam->create.service_handle, @@ -1586,7 +1586,7 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_CREATE_EVT case ESP_GATTS_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; @@ -1617,7 +1617,7 @@ void BLEUtils::dumpGattServerEvent( break; } - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]", + ESP_LOGV(LOG_TAG, "[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(), @@ -1628,14 +1628,14 @@ void BLEUtils::dumpGattServerEvent( case ESP_GATTS_MTU_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, mtu: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, mtu: %d]", evtParam->mtu.conn_id, evtParam->mtu.mtu); break; } // ESP_GATTS_MTU_EVT case ESP_GATTS_READ_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]", + ESP_LOGV(LOG_TAG, "[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(), @@ -1646,14 +1646,14 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_READ_EVT case ESP_GATTS_RESPONSE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, handle: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, handle: 0x%.2x]", gattStatusToString(evtParam->rsp.status).c_str(), evtParam->rsp.handle); break; } // ESP_GATTS_RESPONSE_EVT case ESP_GATTS_REG_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, app_id: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, app_id: %d]", gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; @@ -1666,7 +1666,7 @@ void BLEUtils::dumpGattServerEvent( // - esp_gatt_status_t status // - uint16_t service_handle case ESP_GATTS_START_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", gattStatusToString(evtParam->start.status).c_str(), evtParam->start.service_handle); break; @@ -1686,7 +1686,7 @@ void BLEUtils::dumpGattServerEvent( // - uint16_t len – The length of the incoming value part. // - uint8_t* value – The data for this value part. case ESP_GATTS_WRITE_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]", + ESP_LOGV(LOG_TAG, "[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(), @@ -1696,13 +1696,13 @@ void BLEUtils::dumpGattServerEvent( evtParam->write.is_prep, evtParam->write.len); char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); - ESP_LOGD(LOG_TAG, "[Data: %s]", pHex); + ESP_LOGV(LOG_TAG, "[Data: %s]", pHex); free(pHex); break; } // ESP_GATTS_WRITE_EVT default: - ESP_LOGD(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); + ESP_LOGV(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); break; } } // dumpGattServerEvent @@ -1726,7 +1726,7 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; default: - ESP_LOGD(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); + ESP_LOGV(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; } } // eventTypeToString @@ -1795,7 +1795,7 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT"; default: - ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + ESP_LOGV(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } } // gapEventToString @@ -2001,7 +2001,7 @@ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { case ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT: return "ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT"; default: - ESP_LOGD(LOG_TAG, "Unknown event type: 0x%x", searchEvt); + ESP_LOGV(LOG_TAG, "Unknown event type: 0x%x", searchEvt); return "Unknown event type"; } } // searchEventTypeToString From ae045aa4ef0881ef66ae749f2f6607b671a75970 Mon Sep 17 00:00:00 2001 From: chegewara Date: Sat, 17 Nov 2018 10:16:10 +0100 Subject: [PATCH 288/310] Fix issues in last commit --- cpp_utils/BLEAdvertising.cpp | 186 +++++++++++++++++++++++++------- cpp_utils/BLEAdvertising.h | 13 ++- cpp_utils/BLECharacteristic.cpp | 2 +- cpp_utils/BLEClient.cpp | 3 +- cpp_utils/BLERemoteService.h | 1 + cpp_utils/BLEServer.h | 1 + cpp_utils/BLEServiceMap.cpp | 2 +- cpp_utils/BLEUUID.cpp | 2 - cpp_utils/BLEUtils.cpp | 4 +- 9 files changed, 164 insertions(+), 50 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index d4fd6332..0ad0da78 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -23,14 +23,21 @@ #include #include "BLEUtils.h" #include "GeneralUtils.h" +#include "string.h" +#include "mbedtls/md.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" + + +#define BLOCK_SIZE 64 +#define HMAC MBEDTLS_MD_SHA256 #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif - +static bool is_advertising = false; static const char* LOG_TAG = "BLEAdvertising"; - /** * @brief Construct a default advertising object. * @@ -38,7 +45,7 @@ static const char* LOG_TAG = "BLEAdvertising"; BLEAdvertising::BLEAdvertising() { m_advData.set_scan_rsp = false; m_advData.include_name = true; - m_advData.include_txpower = true; + m_advData.include_txpower = false; m_advData.min_interval = 0x20; m_advData.max_interval = 0x40; m_advData.appearance = 0x00; @@ -61,6 +68,65 @@ BLEAdvertising::BLEAdvertising() { m_customScanResponseData = false; // No custom scan response data } // BLEAdvertising +void BLEAdvertising::setPrivateAddress(esp_ble_addr_type_t type) { + esp_bd_addr_t addr; + m_advParams.own_addr_type = type; + + if(type == BLE_ADDR_TYPE_RPA_PUBLIC) { + esp_ble_gap_config_local_privacy(true); + return; + } + else{ + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + mbedtls_md_context_t ctx; + + char pers[] = "aes generate key"; + int ret; + unsigned char key[BLOCK_SIZE] = {0}; + const char inp[] = "random static address"; + unsigned char outp[BLOCK_SIZE/2]; + + mbedtls_entropy_init( &entropy ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + + if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (unsigned char *) pers, strlen( pers ) ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret ); + goto exit; + } + + if( ( ret = mbedtls_ctr_drbg_random( &ctr_drbg, key, BLOCK_SIZE ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret ); + goto exit; + } + + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(HMAC), true); + mbedtls_md_hmac_starts(&ctx, (const unsigned char *)key, BLOCK_SIZE); + mbedtls_md_hmac_update(&ctx, (const unsigned char *)inp, strlen(inp)); + mbedtls_md_hmac_finish(&ctx, outp); + mbedtls_md_free(&ctx); + ESP_LOG_BUFFER_HEX("random key", key, BLOCK_SIZE); + ESP_LOG_BUFFER_HEX("HASH step 2", outp, BLOCK_SIZE/2); + + memcpy(addr, outp, 6); + } + if(type == BLE_ADDR_TYPE_RANDOM) { + addr[0] &= 0x3F; // <--- Format of non-resolvable private address 00xx xxxx + } + else{ + addr[0] |= 0xC0; // <--- Format of static address 11xx xxxx + } + + esp_ble_gap_set_rand_addr(addr); + ESP_LOG_BUFFER_HEX("random address", addr, 6); + exit: + return; +} + /** * @brief Add a service uuid to exposed list of services. @@ -107,7 +173,7 @@ void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ -void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; @@ -173,20 +239,21 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) // representations. If we have 1 or more services to advertise then we allocate enough // storage to host them and then copy them in one at a time into the contiguous storage. int numServices = m_serviceUUIDs.size(); if (numServices > 0) { - m_advData.service_uuid_len = 16 * numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + m_advData.service_uuid_len = 16*numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; uint8_t* p = m_advData.p_service_uuid; - for (int i = 0; i < numServices; i++) { + for (int i=0; iuuid.uuid128, 16); - p += 16; + p+=16; } } else { m_advData.service_uuid_len = 0; @@ -195,36 +262,45 @@ void BLEAdvertising::start() { esp_err_t errRc; - if (!m_customAdvData) { + if(!is_advertising){ + + // m_semaphoreSetAdv.take("config_adv"); + if (m_customAdvData == false) { // Set the configuration for advertising. - m_advData.set_scan_rsp = false; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + m_advData.set_scan_rsp = false; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + // m_semaphoreSetAdv.give(); + return; + } } - } - if (!m_customScanResponseData) { - m_advData.set_scan_rsp = true; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + // m_semaphoreSetAdv.take("config_rsp"); + if (m_customScanResponseData == false) { + m_advData.set_scan_rsp = true; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + // m_semaphoreSetAdv.give(); + return; + } } } - // If we had services to advertise then we previously allocated some storage for them. // Here we release that storage. if (m_advData.service_uuid_len > 0) { delete[] m_advData.p_service_uuid; m_advData.p_service_uuid = nullptr; } + is_advertising = true; + // m_semaphoreSetAdv.take("start"); // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + // m_semaphoreSetAdv.give(); return; } ESP_LOGD(LOG_TAG, "<< start"); @@ -238,6 +314,7 @@ void BLEAdvertising::start() { */ void BLEAdvertising::stop() { ESP_LOGD(LOG_TAG, ">> stop"); + // m_semaphoreSetAdv.take("stop"); esp_err_t errRc = ::esp_ble_gap_stop_advertising(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -269,7 +346,8 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); + addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); + esp_ble_gap_config_local_icon(appearance); } // setAppearance @@ -279,12 +357,12 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { */ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { char cdata[2]; - switch (uuid.bitSize()) { + switch(uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); break; } @@ -292,7 +370,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); break; } @@ -300,7 +378,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 - addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); break; } @@ -340,7 +418,7 @@ void BLEAdvertisementData::setManufacturerData(std::string data) { char cdata[2]; cdata[0] = data.length() + 1; cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff - addData(std::string(cdata, 2) + data); + addData(std::string(cdata, 2) + data); ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData @@ -354,7 +432,7 @@ void BLEAdvertisementData::setName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setName"); } // setName @@ -365,12 +443,12 @@ void BLEAdvertisementData::setName(std::string name) { */ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { char cdata[2]; - switch (uuid.bitSize()) { + switch(uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); break; } @@ -378,7 +456,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); break; } @@ -386,7 +464,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); break; } @@ -403,12 +481,12 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { */ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { char cdata[2]; - switch (uuid.bitSize()) { + switch(uuid.bitSize()) { case 16: { // [Len] [0x16] [UUID16] data cdata[0] = data.length() + 3; cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data); break; } @@ -416,7 +494,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x20] [UUID32] data cdata[0] = data.length() + 5; cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); + addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data); break; } @@ -424,7 +502,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x21] [UUID128] data cdata[0] = data.length() + 17; cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); + addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data); break; } @@ -443,11 +521,12 @@ void BLEAdvertisementData::setShortName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setShortName"); } // setShortName + /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. @@ -456,5 +535,34 @@ std::string BLEAdvertisementData::getPayload() { return m_payload; } // getPayload +void BLEAdvertising::gapEventHandler( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "gapEventHandler [event no: %d]", (int)event); + + switch(event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { + ESP_LOGI(LOG_TAG, "STOP advertising"); + start(); + break; + } + default: + break; + } +} + -#endif /* CONFIG_BT_ENABLED */ +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index f7cfc240..82f35e6e 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -12,6 +12,7 @@ #include #include "BLEUUID.h" #include +#include "FreeRTOS.h" /** * @brief Advertisement data set by the programmer to be published by the %BLE server. @@ -19,6 +20,7 @@ class BLEAdvertisementData { // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will // be exposed on demand/request or as time permits. + // public: void setAppearance(uint16_t appearance); void setCompleteServices(BLEUUID uuid); @@ -55,13 +57,18 @@ class BLEAdvertising { void setAdvertisementData(BLEAdvertisementData& advertisementData); void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); void setScanResponseData(BLEAdvertisementData& advertisementData); + void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM); + + void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); private: esp_ble_adv_data_t m_advData; esp_ble_adv_params_t m_advParams; std::vector m_serviceUUIDs; - bool m_customAdvData; // Are we using custom advertising data? - bool m_customScanResponseData; // Are we using custom scan response data? + bool m_customAdvData = false; // Are we using custom advertising data? + bool m_customScanResponseData = false; // Are we using custom scan response data? + FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); + }; #endif /* CONFIG_BT_ENABLED */ -#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ +#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ \ No newline at end of file diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index f3cf02b8..3acb70a5 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -427,7 +427,7 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t conn_id – The connection used. // case ESP_GATTS_CONF_EVT: { - ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); + // ESP_LOGD(LOG_TAG, "m_handle = %d", m_handle); if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet m_semaphoreConfEvt.give(param->conf.status); break; diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 4408b140..66baf166 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -414,8 +414,7 @@ std::map* BLEClient::getServices() { // TODO implement retrieving services from cache clearServices(); // Clear any services that may exist. - ESP_LOGD(LOG_TAG, "esp_ble_gattc_get_service: %d services from peer device", count); - errRc = esp_ble_gattc_search_service( + esp_err_t errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), NULL // Filter UUID diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 0f45bd0a..2ab86738 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -34,6 +34,7 @@ class BLERemoteService { BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. std::map* getCharacteristics(); std::map* getCharacteristicsByHandle(); // Get the characteristics map. + void getCharacteristics(std::map* pCharacteristicMap); BLEClient* getClient(void); // Get a reference to the client associated with this service. uint16_t getHandle(); // Get the handle of this service. diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 1dbcab65..23d602a8 100755 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -48,6 +48,7 @@ class BLEServiceMap { BLEService* getFirst(); BLEService* getNext(); void removeService(BLEService *service); + int getRegisteredServiceCount(); private: std::map m_handleMap; diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp index c749be16..cf4f75f4 100755 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -25,7 +25,7 @@ BLEService* BLEServiceMap::getByUUID(const char* uuid) { * @param [in] UUID The UUID to look up the service. * @return The characteristic. */ -BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { +BLEService* BLEServiceMap::getByUUID(BLEUUID uuid, uint8_t inst_id) { for (auto &myPair : m_uuidMap) { if (myPair.first->getUUID().equals(uuid)) { return myPair.first; diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index ee8b6c9d..fd0f5ad5 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -95,8 +95,6 @@ BLEUUID::BLEUUID(std::string value) { m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; i+=2; } - } - } } else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) m_uuid.len = ESP_UUID_LEN_128; diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index d6384931..b503b7ca 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -1001,8 +1001,8 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType 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"; + // case ESP_GATTS_SEND_SERVICE_CHANGE_EVT: + // return "ESP_GATTS_SEND_SERVICE_CHANGE_EVT"; default: return "Unknown"; } From 011ea72506b7287a3b3a0742d68e3bb7cc6d276c Mon Sep 17 00:00:00 2001 From: chegewara Date: Mon, 19 Nov 2018 15:07:05 +0100 Subject: [PATCH 289/310] Few more fixes, arduino compatibility tested --- cpp_utils/BLEAdvertising.cpp | 179 ++++++++++---------------------- cpp_utils/BLEAdvertising.h | 6 +- cpp_utils/BLECharacteristic.cpp | 29 +++--- cpp_utils/BLECharacteristic.h | 4 +- cpp_utils/BLEDescriptor.cpp | 60 ++--------- cpp_utils/BLEDescriptor.h | 4 +- cpp_utils/BLEDescriptorMap.cpp | 16 +-- cpp_utils/BLEDevice.cpp | 2 +- cpp_utils/BLEService.cpp | 1 - cpp_utils/BLEUtils.cpp | 50 ++++++--- cpp_utils/GeneralUtils.cpp | 4 + 11 files changed, 137 insertions(+), 218 deletions(-) diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 0ad0da78..41ad87a2 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -23,21 +23,14 @@ #include #include "BLEUtils.h" #include "GeneralUtils.h" -#include "string.h" -#include "mbedtls/md.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" - - -#define BLOCK_SIZE 64 -#define HMAC MBEDTLS_MD_SHA256 #ifdef ARDUINO_ARCH_ESP32 #include "esp32-hal-log.h" #endif -static bool is_advertising = false; + static const char* LOG_TAG = "BLEAdvertising"; + /** * @brief Construct a default advertising object. * @@ -45,7 +38,7 @@ static const char* LOG_TAG = "BLEAdvertising"; BLEAdvertising::BLEAdvertising() { m_advData.set_scan_rsp = false; m_advData.include_name = true; - m_advData.include_txpower = false; + m_advData.include_txpower = true; m_advData.min_interval = 0x20; m_advData.max_interval = 0x40; m_advData.appearance = 0x00; @@ -63,70 +56,12 @@ BLEAdvertising::BLEAdvertising() { m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; m_advParams.channel_map = ADV_CHNL_ALL; m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC; m_customAdvData = false; // No custom advertising data m_customScanResponseData = false; // No custom scan response data } // BLEAdvertising -void BLEAdvertising::setPrivateAddress(esp_ble_addr_type_t type) { - esp_bd_addr_t addr; - m_advParams.own_addr_type = type; - - if(type == BLE_ADDR_TYPE_RPA_PUBLIC) { - esp_ble_gap_config_local_privacy(true); - return; - } - else{ - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_context entropy; - mbedtls_md_context_t ctx; - - char pers[] = "aes generate key"; - int ret; - unsigned char key[BLOCK_SIZE] = {0}; - const char inp[] = "random static address"; - unsigned char outp[BLOCK_SIZE/2]; - - mbedtls_entropy_init( &entropy ); - mbedtls_ctr_drbg_init( &ctr_drbg ); - - if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, - (unsigned char *) pers, strlen( pers ) ) ) != 0 ) - { - printf( " failed\n ! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret ); - goto exit; - } - - if( ( ret = mbedtls_ctr_drbg_random( &ctr_drbg, key, BLOCK_SIZE ) ) != 0 ) - { - printf( " failed\n ! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret ); - goto exit; - } - - mbedtls_md_init(&ctx); - mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(HMAC), true); - mbedtls_md_hmac_starts(&ctx, (const unsigned char *)key, BLOCK_SIZE); - mbedtls_md_hmac_update(&ctx, (const unsigned char *)inp, strlen(inp)); - mbedtls_md_hmac_finish(&ctx, outp); - mbedtls_md_free(&ctx); - ESP_LOG_BUFFER_HEX("random key", key, BLOCK_SIZE); - ESP_LOG_BUFFER_HEX("HASH step 2", outp, BLOCK_SIZE/2); - - memcpy(addr, outp, 6); - } - if(type == BLE_ADDR_TYPE_RANDOM) { - addr[0] &= 0x3F; // <--- Format of non-resolvable private address 00xx xxxx - } - else{ - addr[0] |= 0xC0; // <--- Format of static address 11xx xxxx - } - - esp_ble_gap_set_rand_addr(addr); - ESP_LOG_BUFFER_HEX("random address", addr, 6); - exit: - return; -} - /** * @brief Add a service uuid to exposed list of services. @@ -158,22 +93,31 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { } // setAppearance void BLEAdvertising::setMinInterval(uint16_t mininterval) { - m_advData.min_interval = mininterval; m_advParams.adv_int_min = mininterval; } // setMinInterval void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { - m_advData.max_interval = maxinterval; m_advParams.adv_int_max = maxinterval; } // setMaxInterval +void BLEAdvertising::setMinPreferred(uint16_t mininterval) { + m_advData.min_interval = mininterval; +} // + +void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + m_advData.max_interval = maxinterval; +} // + +void BLEAdvertising::setScanResponse(bool set) { + m_scanResp = set; +} /** * @brief Set the filtering for the scan filter. * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ -void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; @@ -239,21 +183,20 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); - // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) // representations. If we have 1 or more services to advertise then we allocate enough // storage to host them and then copy them in one at a time into the contiguous storage. int numServices = m_serviceUUIDs.size(); if (numServices > 0) { - m_advData.service_uuid_len = 16*numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + m_advData.service_uuid_len = 16 * numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; uint8_t* p = m_advData.p_service_uuid; - for (int i=0; iuuid.uuid128, 16); - p+=16; + p += 16; } } else { m_advData.service_uuid_len = 0; @@ -262,45 +205,40 @@ void BLEAdvertising::start() { esp_err_t errRc; - if(!is_advertising){ - - // m_semaphoreSetAdv.take("config_adv"); - if (m_customAdvData == false) { + if (!m_customAdvData) { // Set the configuration for advertising. - m_advData.set_scan_rsp = false; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - // m_semaphoreSetAdv.give(); - return; - } + m_advData.set_scan_rsp = false; + m_advData.include_name = !m_scanResp; + m_advData.include_txpower = !m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; } + } - // m_semaphoreSetAdv.take("config_rsp"); - if (m_customScanResponseData == false) { - m_advData.set_scan_rsp = true; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - // m_semaphoreSetAdv.give(); - return; - } + if (!m_customScanResponseData && m_scanResp) { + m_advData.set_scan_rsp = true; + m_advData.include_name = m_scanResp; + m_advData.include_txpower = m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; } } + // If we had services to advertise then we previously allocated some storage for them. // Here we release that storage. if (m_advData.service_uuid_len > 0) { delete[] m_advData.p_service_uuid; m_advData.p_service_uuid = nullptr; } - is_advertising = true; - // m_semaphoreSetAdv.take("start"); // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - // m_semaphoreSetAdv.give(); return; } ESP_LOGD(LOG_TAG, "<< start"); @@ -314,7 +252,6 @@ void BLEAdvertising::start() { */ void BLEAdvertising::stop() { ESP_LOGD(LOG_TAG, ">> stop"); - // m_semaphoreSetAdv.take("stop"); esp_err_t errRc = ::esp_ble_gap_stop_advertising(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -346,8 +283,7 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char *)&appearance,2)); - esp_ble_gap_config_local_icon(appearance); + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); } // setAppearance @@ -357,12 +293,12 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) { */ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); break; } @@ -370,7 +306,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); break; } @@ -378,7 +314,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); break; } @@ -418,7 +354,7 @@ void BLEAdvertisementData::setManufacturerData(std::string data) { char cdata[2]; cdata[0] = data.length() + 1; cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff - addData(std::string(cdata, 2) + data); + addData(std::string(cdata, 2) + data); ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData @@ -432,7 +368,7 @@ void BLEAdvertisementData::setName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setName"); } // setName @@ -443,12 +379,12 @@ void BLEAdvertisementData::setName(std::string name) { */ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); break; } @@ -456,7 +392,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); break; } @@ -464,7 +400,7 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16)); + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); break; } @@ -481,12 +417,12 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { */ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { char cdata[2]; - switch(uuid.bitSize()) { + switch (uuid.bitSize()) { case 16: { // [Len] [0x16] [UUID16] data cdata[0] = data.length() + 3; cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); break; } @@ -494,7 +430,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x20] [UUID32] data cdata[0] = data.length() + 5; cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 - addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); break; } @@ -502,7 +438,7 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { // [Len] [0x21] [UUID128] data cdata[0] = data.length() + 17; cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 - addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data); + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); break; } @@ -521,12 +457,11 @@ void BLEAdvertisementData::setShortName(std::string name) { char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 - addData(std::string(cdata, 2) + name); + addData(std::string(cdata, 2) + name); ESP_LOGD("BLEAdvertisementData", "<< setShortName"); } // setShortName - /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. @@ -535,11 +470,11 @@ std::string BLEAdvertisementData::getPayload() { return m_payload; } // getPayload -void BLEAdvertising::gapEventHandler( +void BLEAdvertising::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { - ESP_LOGD(LOG_TAG, "gapEventHandler [event no: %d]", (int)event); + ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event); switch(event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { @@ -565,4 +500,4 @@ void BLEAdvertising::gapEventHandler( } -#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file +#endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h index 82f35e6e..3128b50f 100644 --- a/cpp_utils/BLEAdvertising.h +++ b/cpp_utils/BLEAdvertising.h @@ -59,7 +59,10 @@ class BLEAdvertising { void setScanResponseData(BLEAdvertisementData& advertisementData); void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM); - void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); + void setScanResponse(bool); private: esp_ble_adv_data_t m_advData; @@ -68,6 +71,7 @@ class BLEAdvertising { bool m_customAdvData = false; // Are we using custom advertising data? bool m_customScanResponseData = false; // Are we using custom scan response data? FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); + bool m_scanResp = true; }; #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 3acb70a5..bdeee727 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -96,13 +96,12 @@ void BLECharacteristic::executeCreate(BLEService* pService) { esp_attr_control_t control; control.auto_rsp = ESP_GATT_RSP_BY_APP; - + m_semaphoreCreateEvt.take("executeCreate"); esp_err_t errRc = ::esp_ble_gatts_add_char( m_pService->getHandle(), getUUID().getNative(), static_cast(m_permissions), getProperties(), - //&value, nullptr, &control); // Whether to auto respond or not. @@ -110,6 +109,13 @@ void BLECharacteristic::executeCreate(BLEService* pService) { ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } + m_semaphoreCreateEvt.wait("executeCreate"); + + BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + while (pDescriptor != nullptr) { + pDescriptor->executeCreate(this); + pDescriptor = m_descriptorMap.getNext(); + } // End while ESP_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate @@ -244,17 +250,14 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t service_handle // - esp_bt_uuid_t char_uuid case ESP_GATTS_ADD_CHAR_EVT: { - if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) && - getHandle() == param->add_char.attr_handle && - getService()->getHandle()==param->add_char.service_handle) { - + if (getHandle() == param->add_char.attr_handle) { // we have created characteristic, now we can create descriptors - BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); - while (pDescriptor != nullptr) { - pDescriptor->executeCreate(this); - pDescriptor = m_descriptorMap.getNext(); - } // End while - + // BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + // while (pDescriptor != nullptr) { + // pDescriptor->executeCreate(this); + // pDescriptor = m_descriptorMap.getNext(); + // } // End while + m_semaphoreCreateEvt.give(); } break; } // ESP_GATTS_ADD_CHAR_EVT @@ -427,7 +430,7 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t conn_id – The connection used. // case ESP_GATTS_CONF_EVT: { - // ESP_LOGD(LOG_TAG, "m_handle = %d", m_handle); + ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet m_semaphoreConfEvt.give(param->conf.status); break; diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 49af0ba3..5eb1e8d6 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -38,9 +38,9 @@ class BLEDescriptorMap { BLEDescriptor* getFirst(); BLEDescriptor* getNext(); private: - std::map m_uuidMap; + std::map m_uuidMap; std::map m_handleMap; - std::map::iterator m_iterator; + std::map::iterator m_iterator; }; diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 5f23026f..4d7b5efd 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -29,21 +29,21 @@ static const char* LOG_TAG = "BLEDescriptor"; /** * @brief BLEDescriptor constructor. */ -BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) { +BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) { } /** * @brief BLEDescriptor constructor. */ -BLEDescriptor::BLEDescriptor(BLEUUID uuid) { +BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) { m_bleUUID = uuid; m_value.attr_len = 0; // Initial length is 0. - m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; // Maximum length of the data. + m_value.attr_max_len = max_len; // Maximum length of the data. m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCharacteristic = nullptr; // No initial characteristic. m_pCallback = nullptr; // No initial callback. - m_value.attr_value = (uint8_t*) malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. + m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. } // BLEDescriptor @@ -70,12 +70,12 @@ void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. esp_attr_control_t control; - control.auto_rsp = ESP_GATT_RSP_BY_APP; + control.auto_rsp = ESP_GATT_AUTO_RSP; m_semaphoreCreateEvt.take("executeCreate"); esp_err_t errRc = ::esp_ble_gatts_add_char_descr( pCharacteristic->getService()->getHandle(), getUUID().getNative(), - (esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), + (esp_gatt_perm_t)m_permissions, &m_value, &control); if (errRc != ESP_OK) { @@ -143,17 +143,6 @@ void BLEDescriptor::handleGATTServerEvent( // - uint16_t service_handle // - esp_bt_uuid_t char_uuid case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - /* - ESP_LOGD(LOG_TAG, "DEBUG: m_pCharacteristic: %x", (uint32_t)m_pCharacteristic); - ESP_LOGD(LOG_TAG, "DEBUG: m_bleUUID: %s, add_char_descr.char_uuid: %s, equals: %d", - m_bleUUID.toString().c_str(), - BLEUUID(param->add_char_descr.char_uuid).toString().c_str(), - m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid))); - ESP_LOGD(LOG_TAG, "DEBUG: service->getHandle: %x, add_char_descr.service_handle: %x", - m_pCharacteristic->getService()->getHandle(), param->add_char_descr.service_handle); - ESP_LOGD(LOG_TAG, "DEBUG: service->lastCharacteristic: %x", - (uint32_t)m_pCharacteristic->getService()->getLastCreatedCharacteristic()); - */ if (m_pCharacteristic != nullptr && m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) && m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && @@ -180,23 +169,6 @@ void BLEDescriptor::handleGATTServerEvent( if (param->write.handle == m_handle) { setValue(param->write.value, param->write.len); // Set the value of the descriptor. - esp_gatt_rsp_t rsp; // Build a response. - rsp.attr_value.len = getLength(); - rsp.attr_value.handle = m_handle; - rsp.attr_value.offset = 0; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, - param->write.conn_id, - param->write.trans_id, - ESP_GATT_OK, - &rsp); - - if (errRc != ESP_OK) { // Check the return code from the send of the response. - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. m_pCallback->onWrite(this); // Invoke the onWrite callback handler. } @@ -223,26 +195,6 @@ void BLEDescriptor::handleGATTServerEvent( m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. } - if (param->read.need_rsp) { // Do we need a response - ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); - esp_gatt_rsp_t rsp; - rsp.attr_value.len = getLength(); - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.offset = 0; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); - - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, - param->read.conn_id, - param->read.trans_id, - ESP_GATT_OK, - &rsp); - - if (errRc != ESP_OK) { // Check the return code from the send of the response. - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } // End of need a response. } // End of this is our handle break; } // ESP_GATTS_READ_EVT diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index d9e0aefb..03cc5791 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -24,8 +24,8 @@ class BLEDescriptorCallbacks; */ class BLEDescriptor { public: - BLEDescriptor(const char* uuid); - BLEDescriptor(BLEUUID uuid); + BLEDescriptor(const char* uuid, uint16_t max_len = 100); + BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); virtual ~BLEDescriptor(); uint16_t getHandle(); // Get the handle of the descriptor. diff --git a/cpp_utils/BLEDescriptorMap.cpp b/cpp_utils/BLEDescriptorMap.cpp index 075b3025..6b845833 100644 --- a/cpp_utils/BLEDescriptorMap.cpp +++ b/cpp_utils/BLEDescriptorMap.cpp @@ -32,8 +32,8 @@ BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { */ BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) { for (auto &myPair : m_uuidMap) { - if (myPair.second->getUUID().equals(uuid)) { - return myPair.second; + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; } } //return m_uuidMap.at(uuid.toString()); @@ -58,7 +58,7 @@ BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) { * @return N/A. */ void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ - m_uuidMap.insert(std::pair(uuid, pDescriptor)); + m_uuidMap.insert(std::pair(pDescriptor, uuid)); } // setByUUID @@ -70,7 +70,7 @@ void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ * @return N/A. */ void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) { - m_uuidMap.insert(std::pair(uuid.toString(), pDescriptor)); + m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); } // setByUUID @@ -98,7 +98,7 @@ std::string BLEDescriptorMap::toString() { stringStream << "\n"; } count++; - stringStream << "handle: 0x" << std::setw(2) << myPair.second->getHandle() << ", uuid: " + myPair.second->getUUID().toString(); + stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); } return stringStream.str(); } // toString @@ -116,7 +116,7 @@ void BLEDescriptorMap::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every descriptor we have. for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); + myPair.first->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent @@ -128,7 +128,7 @@ void BLEDescriptorMap::handleGATTServerEvent( BLEDescriptor* BLEDescriptorMap::getFirst() { m_iterator = m_uuidMap.begin(); if (m_iterator == m_uuidMap.end()) return nullptr; - BLEDescriptor* pRet = m_iterator->second; + BLEDescriptor* pRet = m_iterator->first; m_iterator++; return pRet; } // getFirst @@ -140,7 +140,7 @@ BLEDescriptor* BLEDescriptorMap::getFirst() { */ BLEDescriptor* BLEDescriptorMap::getNext() { if (m_iterator == m_uuidMap.end()) return nullptr; - BLEDescriptor* pRet = m_iterator->second; + BLEDescriptor* pRet = m_iterator->first; m_iterator++; return pRet; } // getNext diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index add7e41e..2745d4c8 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -271,7 +271,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; } if(m_bleAdvertising != nullptr) { - BLEDevice::getAdvertising()->gapEventHandler(event, param); + BLEDevice::getAdvertising()->handleGAPEvent(event, param); } if(m_customGapHandler != nullptr) { diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 9e8b05ae..7e7d67ac 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -292,7 +292,6 @@ void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t } pCharacteristic->setHandle(param->add_char.attr_handle); m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); - //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); break; } // Reached the correct service. break; diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index b503b7ca..abec5a8a 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -41,6 +41,7 @@ typedef struct { } member_t; static const member_t members_ids[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 {0xFE08, "Microsoft"}, {0xFE09, "Pillsy, Inc."}, {0xFE0A, "ruwido austria gmbh"}, @@ -290,6 +291,7 @@ static const member_t members_ids[] = { {0xFEFE, "GN ReSound A/S"}, {0xFEFF, "GN Netcom"}, {0xFFFF, "Reserved"}, /*for testing purposes only*/ +#endif {0, "" } }; @@ -299,6 +301,7 @@ typedef struct { } 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"}, @@ -314,6 +317,7 @@ static const gattdescriptor_t g_descriptor_ids[] = { {0x290E,"Time Trigger Setting"}, {0x2906,"Valid Range"}, {0x290A,"Value Trigger Setting"}, +#endif { 0, "" } }; @@ -323,6 +327,7 @@ typedef struct { } 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"}, @@ -539,6 +544,7 @@ static const characteristicMap_t g_characteristicsMappings[] = { {0x2A9D,"Weight Measurement"}, {0x2A9E,"Weight Scale Feature"}, {0x2A79,"Wind Chill"}, +#endif {0, ""} }; @@ -556,6 +562,7 @@ typedef struct { * 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}, @@ -591,6 +598,7 @@ static const gattService_t g_gattServices[] = { {"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 } }; @@ -629,6 +637,7 @@ static std::string gattIdToString(esp_gatt_id_t gattId) { */ 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: @@ -637,6 +646,7 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { 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"; } @@ -679,6 +689,7 @@ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { */ 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 @@ -729,6 +740,7 @@ const char* BLEUtils::advTypeToString(uint8_t advType) { return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; +#endif default: ESP_LOGV(LOG_TAG, " adv data type: 0x%x", advType); return ""; @@ -812,42 +824,35 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { */ 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"; } @@ -857,6 +862,7 @@ std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { 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: @@ -939,6 +945,7 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType return "ESP_GATTC_WRITE_CHAR_EVT"; case ESP_GATTC_WRITE_DESCR_EVT: return "ESP_GATTC_WRITE_DESCR_EVT"; +#endif default: ESP_LOGW(LOG_TAG, "Unknown GATT Client event type: %d", eventType); return "Unknown"; @@ -953,6 +960,7 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType */ 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: @@ -1001,8 +1009,9 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType 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"; + case ESP_GATTS_SEND_SERVICE_CHANGE_EVT: + return "ESP_GATTS_SEND_SERVICE_CHANGE_EVT"; +#endif default: return "Unknown"; } @@ -1016,12 +1025,14 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType */ 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"; } @@ -1036,6 +1047,7 @@ void BLEUtils::dumpGapEvent( esp_ble_gap_cb_param_t* param) { ESP_LOGV(LOG_TAG, "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 @@ -1246,7 +1258,7 @@ void BLEUtils::dumpGapEvent( ESP_LOGV(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); break; } // ESP_GAP_BLE_SEC_REQ_EVT - +#endif default: { ESP_LOGV(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; @@ -1269,6 +1281,7 @@ void BLEUtils::dumpGattClientEvent( //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; ESP_LOGV(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 // ESP_GATTC_CLOSE_EVT // // close: @@ -1492,7 +1505,7 @@ void BLEUtils::dumpGattClientEvent( ); break; } // ESP_GATTC_WRITE_CHAR_EVT - +#endif default: break; } @@ -1515,6 +1528,7 @@ void BLEUtils::dumpGattServerEvent( esp_ble_gatts_cb_param_t* evtParam) { ESP_LOGV(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATTS_ADD_CHAR_DESCR_EVT: { ESP_LOGV(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", @@ -1700,7 +1714,7 @@ void BLEUtils::dumpGattServerEvent( free(pHex); break; } // ESP_GATTS_WRITE_EVT - +#endif default: ESP_LOGV(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); break; @@ -1715,6 +1729,7 @@ void BLEUtils::dumpGattServerEvent( */ 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: @@ -1725,6 +1740,7 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { return "ESP_BLE_EVT_NON_CONN_ADV"; case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; +#endif default: ESP_LOGV(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; @@ -1740,6 +1756,7 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { */ 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: @@ -1794,6 +1811,7 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { 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: ESP_LOGV(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; @@ -1874,6 +1892,7 @@ std::string BLEUtils::gattServiceToString(uint32_t serviceId) { */ 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: @@ -1960,6 +1979,7 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { return "ESP_GATT_PRC_IN_PROGRESS"; case ESP_GATT_OUT_OF_RANGE: return "ESP_GATT_OUT_OF_RANGE"; +#endif default: return "Unknown"; } @@ -1986,6 +2006,7 @@ std::string BLEUtils::getMember(uint32_t memberId) { */ 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: @@ -2000,6 +2021,7 @@ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { 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: ESP_LOGV(LOG_TAG, "Unknown event type: 0x%x", searchEvt); return "Unknown event type"; diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 8d58d4eb..dbbc65be 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -365,6 +365,7 @@ std::vector GeneralUtils::split(std::string source, char delimiter) */ const char* GeneralUtils::errorToString(esp_err_t errCode) { switch (errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_OK: return "ESP_OK"; case ESP_FAIL: @@ -431,6 +432,7 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "ESP_ERR_WIFI_TIMEOUT"; case ESP_ERR_WIFI_WAKE_FAIL: return "ESP_ERR_WIFI_WAKE_FAIL"; +#endif default: return "Unknown ESP_ERR error"; } @@ -448,6 +450,7 @@ const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { if (errCode == UINT8_MAX) return "Not Connected (default value)"; switch ((wifi_err_reason_t) errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 case WIFI_REASON_UNSPECIFIED: return "WIFI_REASON_UNSPECIFIED"; case WIFI_REASON_AUTH_EXPIRE: @@ -504,6 +507,7 @@ const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { return "WIFI_REASON_ASSOC_FAIL"; case WIFI_REASON_HANDSHAKE_TIMEOUT: return "WIFI_REASON_HANDSHAKE_TIMEOUT"; +#endif default: return "Unknown ESP_ERR error"; } From 91f46d723cd9b54cfeba9e7cda6c2406cd3b5174 Mon Sep 17 00:00:00 2001 From: rkone Date: Tue, 27 Nov 2018 11:11:25 -0500 Subject: [PATCH 290/310] fix duplicate close parens --- cpp_utils/BLEUUID.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index ee8b6c9d..fd0f5ad5 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -95,8 +95,6 @@ BLEUUID::BLEUUID(std::string value) { m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; i+=2; } - } - } } else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) m_uuid.len = ESP_UUID_LEN_128; From 8687fc40e9953dc876dccab401278b0f3cb26dfe Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 27 Nov 2018 17:25:01 +0100 Subject: [PATCH 291/310] Few bugixes, compatibility changes and requested functionalities --- cpp_utils/BLEAdvertisedDevice.cpp | 14 +++---- cpp_utils/BLEAdvertisedDevice.h | 3 +- cpp_utils/BLECharacteristic.cpp | 2 +- cpp_utils/BLEClient.cpp | 27 +++++++------ cpp_utils/BLEDevice.cpp | 5 ++- cpp_utils/BLERemoteCharacteristic.cpp | 57 ++++++++++++++------------- cpp_utils/BLEScan.cpp | 12 +++++- cpp_utils/BLEServer.cpp | 13 ++++++ cpp_utils/BLEServer.h | 3 +- cpp_utils/BLEUtils.cpp | 2 +- 10 files changed, 84 insertions(+), 54 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index dbda9172..41d5faba 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -233,7 +233,8 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) uint8_t ad_type; uint8_t sizeConsumed = 0; bool finished = false; - setPayload(payload); + m_payload = payload; + m_payloadLength = total_len; while(!finished) { length = *payload; // Retrieve the length of the record. @@ -351,9 +352,9 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) } // Length <> 0 - if (sizeConsumed >= total_len || length == 0) { + if (sizeConsumed >= total_len) finished = true; - } + } // !finished } // parseAdvertisement @@ -510,10 +511,6 @@ uint8_t* BLEAdvertisedDevice::getPayload() { return m_payload; } -void BLEAdvertisedDevice::setPayload(uint8_t* payload) { - m_payload = payload; -} - esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() { return m_addressType; } @@ -522,6 +519,9 @@ void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) { m_addressType = type; } +size_t BLEAdvertisedDevice::getPayloadLength() { + return m_payloadLength; +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index aaa1417c..aec83746 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -40,6 +40,7 @@ class BLEAdvertisedDevice { BLEUUID getServiceUUID(); int8_t getTXPower(); uint8_t* getPayload(); + size_t getPayloadLength(); esp_ble_addr_type_t getAddressType(); void setAddressType(esp_ble_addr_type_t type); @@ -72,7 +73,6 @@ class BLEAdvertisedDevice { void setServiceUUID(const char* serviceUUID); void setServiceUUID(BLEUUID serviceUUID); void setTXPower(int8_t txPower); - void setPayload(uint8_t* payload); bool m_haveAppearance; bool m_haveManufacturerData; @@ -96,6 +96,7 @@ class BLEAdvertisedDevice { std::string m_serviceData; BLEUUID m_serviceDataUUID; uint8_t* m_payload; + size_t m_payloadLength = 0; esp_ble_addr_type_t m_addressType; }; diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index bdeee727..dee4454a 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -430,7 +430,7 @@ void BLECharacteristic::handleGATTServerEvent( // - uint16_t conn_id – The connection used. // case ESP_GATTS_CONF_EVT: { - ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); + // ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet m_semaphoreConfEvt.give(param->conf.status); break; diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 66baf166..8009f408 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -102,7 +102,7 @@ bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { // We need the connection handle that we get from registering the application. We register the app // and then block on its completion. When the event has arrived, we will have the handle. m_appId = BLEDevice::m_appId++; - BLEDevice::addPeerDevice(this, true, ESP_GATT_IF_NONE); + BLEDevice::addPeerDevice(this, true, m_appId); m_semaphoreRegEvt.take("connect"); // clearServices(); // we dont need to delete services since every client is unique? @@ -119,10 +119,10 @@ bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { // Perform the open connection request against the target BLE Server. m_semaphoreOpenEvt.take("connect"); errRc = ::esp_ble_gattc_open( - getGattcIf(), + m_gattc_if, *getPeerAddress().getNative(), // address type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. - 1 // direct connection + 1 // direct connection <-- maybe needs to be changed in case of direct indirect connection??? ); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -184,10 +184,12 @@ void BLEClient::gattClientEventHandler( case ESP_GATTC_DISCONNECT_EVT: { // If we receive a disconnect event, set the class flag that indicates that we are // no longer connected. + m_isConnected = false; if (m_pClientCallbacks != nullptr) { m_pClientCallbacks->onDisconnect(this); } - m_isConnected = false; + BLEDevice::removePeerDevice(m_appId, true); + esp_ble_gattc_app_unregister(m_gattc_if); m_semaphoreRssiCmplEvt.give(); m_semaphoreSearchCmplEvt.give(1); break; @@ -261,14 +263,15 @@ void BLEClient::gattClientEventHandler( ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status); break; } -#ifndef ARDUINO_ARCH_ESP32 - if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) { - ESP_LOGI(LOG_TAG, "Get service information from remote device"); - } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) { - ESP_LOGI(LOG_TAG, "Get service information from flash"); - } else { - ESP_LOGI(LOG_TAG, "unknown service source"); - } +#ifndef ARDUINO_ARCH_ESP32 +// commented out just for now to keep backward compatibility + // if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) { + // ESP_LOGI(LOG_TAG, "Get service information from remote device"); + // } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) { + // ESP_LOGI(LOG_TAG, "Get service information from flash"); + // } else { + // ESP_LOGI(LOG_TAG, "unknown service source"); + // } #endif m_semaphoreSearchCmplEvt.give(0); break; diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 2745d4c8..33efabdb 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -603,7 +603,8 @@ void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) { ESP_LOGI(LOG_TAG, "remove: %d, GATT role %s", conn_id, _client?"client":"server"); - m_connectedClientsMap.erase(conn_id); + if(m_connectedClientsMap.find(conn_id) != m_connectedClientsMap.end()) + m_connectedClientsMap.erase(conn_id); } /* multi connect support */ @@ -621,7 +622,7 @@ void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) { esp_bt_controller_deinit(); #ifndef ARDUINO_ARCH_ESP32 if (release_memory) { - esp_bt_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it) + esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it) } else { initialized = false; } diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 1768dbb6..21d91b47 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -473,7 +473,7 @@ void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, } uint8_t val[] = {0x00, 0x00}; - BLERemoteDescriptor* desc = getDescriptor(BLEUUID("0x2902")); + BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902); desc->writeValue(val, 2); } // End Unregister @@ -520,7 +520,32 @@ std::string BLERemoteCharacteristic::toString() { * @return N/A. */ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { - ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", newValue.length()); + writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic. + * + * This is a convenience function. Many BLE characteristics are a single byte of data. + * @param [in] newValue The new byte value to write. + * @param [in] response Whether we require a response from the write. + * @return N/A. + */ +void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { + writeValue(&newValue, 1, response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + */ +void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { + // writeValue(std::string((char*)data, length), response); + ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); // Check to see that we are connected. if (!getRemoteService()->getClient()->isConnected()) { @@ -534,8 +559,8 @@ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { m_pRemoteService->getClient()->getGattcIf(), m_pRemoteService->getClient()->getConnId(), getHandle(), - newValue.length(), - (uint8_t*)newValue.data(), + length, + data, response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE ); @@ -550,30 +575,6 @@ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { ESP_LOGD(LOG_TAG, "<< writeValue"); } // writeValue - -/** - * @brief Write the new value for the characteristic. - * - * This is a convenience function. Many BLE characteristics are a single byte of data. - * @param [in] newValue The new byte value to write. - * @param [in] response Whether we require a response from the write. - * @return N/A. - */ -void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { - writeValue(std::string(reinterpret_cast(&newValue), 1), response); -} // writeValue - - -/** - * @brief Write the new value for the characteristic from a data buffer. - * @param [in] data A pointer to a data buffer. - * @param [in] length The length of the data in the data buffer. - * @param [in] response Whether we require a response from the write. - */ -void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { - writeValue(std::string((char*)data, length), response); -} // writeValue - /** * @brief Read raw data from remote characteristic as hex bytes * @return return pointer data read diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 7164626b..e1778b84 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -101,7 +101,7 @@ void BLEScan::handleGAPEvent( if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str()); - vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we will blocked here + vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here break; } @@ -314,5 +314,15 @@ BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) { return dev; } +BLEScanResults BLEScan::getResults() { + return m_scanResults; +} + +void BLEScan::clearResults() { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 63962027..9b52762d 100755 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -405,4 +405,17 @@ void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { m_connectedServersMap.erase(conn_id); } /* multi connect support */ + +/** + * Update connection parameters can be called only after connection has been established + */ +void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { + esp_ble_conn_update_params_t conn_params; + memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); + conn_params.latency = latency; + conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms + conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms + conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms + esp_ble_gap_update_conn_params(&conn_params); +} #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index 23d602a8..d39d8bfe 100755 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -73,6 +73,7 @@ class BLEServer { BLEService* getServiceByUUID(BLEUUID uuid); bool connect(BLEAddress address); uint16_t m_appId; + void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); /* multi connection support */ std::map getPeerDevices(bool client); @@ -81,6 +82,7 @@ class BLEServer { BLEServer* getServerByConnId(uint16_t conn_id); void updatePeerMTU(uint16_t connId, uint16_t mtu); uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); private: @@ -102,7 +104,6 @@ class BLEServer { BLEServerCallbacks* m_pServerCallbacks = nullptr; void createApp(uint16_t appId); - uint16_t getConnId(); uint16_t getGattsIf(); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void registerApp(uint16_t); diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index abec5a8a..02cdf9ae 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -947,7 +947,7 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType return "ESP_GATTC_WRITE_DESCR_EVT"; #endif default: - ESP_LOGW(LOG_TAG, "Unknown GATT Client event type: %d", eventType); + ESP_LOGV(LOG_TAG, "Unknown GATT Client event type: %d", eventType); return "Unknown"; } } // gattClientEventTypeToString From ba964d5dd7a61ef0ba89d340399b0dc29011ec82 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 27 Nov 2018 18:52:18 +0100 Subject: [PATCH 292/310] Add missing BLEScan.h --- cpp_utils/BLEScan.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index ae4663e1..2f71a727 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -58,7 +58,8 @@ class BLEScan { BLEScanResults start(uint32_t duration, bool is_continue = false); void stop(); void erase(BLEAddress address); - // void setPower(esp_power_level_t powerLevel); + BLEScanResults getResults(); + void clearResults(); private: BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. From 0d01a6402ca404cce499b77c5c9c39055d204226 Mon Sep 17 00:00:00 2001 From: rkone Date: Fri, 7 Dec 2018 14:58:17 -0500 Subject: [PATCH 293/310] uint8_t < 256 is always true --- cpp_utils/MMU.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp_utils/MMU.cpp b/cpp_utils/MMU.cpp index 57c0ccce..ee7adb59 100644 --- a/cpp_utils/MMU.cpp +++ b/cpp_utils/MMU.cpp @@ -47,7 +47,7 @@ static uint32_t flashPageToOffset(uint32_t page) { const uint32_t mappingInvalid = 1 << 8; printf("PRO CPU MMU\n"); - for (uint8_t i = 0; i < 256; i++) { + for (uint16_t i = 0; i < 256; i++) { if (!(DPORT_PRO_FLASH_MMU_TABLE[i] & mappingInvalid)) { addressRange_t addressRange = entryNumberToAddressRange(i); printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", @@ -59,7 +59,7 @@ static uint32_t flashPageToOffset(uint32_t page) { } printf("\n"); printf("APP CPU MMU\n"); - for (uint8_t i = 0; i < 256; i++) { + for (uint16_t i = 0; i < 256; i++) { if (!(DPORT_APP_FLASH_MMU_TABLE[i] & mappingInvalid)) { addressRange_t addressRange = entryNumberToAddressRange(i); printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", From ab386db94ca1a836da1f01f3ebe495173597ebea Mon Sep 17 00:00:00 2001 From: Markus Keppeler Date: Fri, 14 Dec 2018 16:54:48 +0100 Subject: [PATCH 294/310] Add access to remote address in BLERemoteCharacteristic --- cpp_utils/BLERemoteCharacteristic.cpp | 8 ++++++++ cpp_utils/BLERemoteCharacteristic.h | 1 + 2 files changed, 9 insertions(+) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 21d91b47..db52cdc5 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -582,5 +582,13 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp uint8_t* BLERemoteCharacteristic::readRawData() { return m_rawData; } +/** + * @brief Get the address of the remote server + * @return RemoteAddress + */ +BLEAddress BLERemoteCharacteristic::getRemoteAddress(){ + return m_pRemoteService->getClient()->getPeerAddress(); +} + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index fbcafe8d..47517fff 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -51,6 +51,7 @@ class BLERemoteCharacteristic { void writeValue(uint8_t newValue, bool response = false); std::string toString(); uint8_t* readRawData(); + BLEAddress getRemoteAddress(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); From 5d616801938d066f52e2fcb925cedcfef87d3b4e Mon Sep 17 00:00:00 2001 From: luisan00 Date: Mon, 17 Dec 2018 11:48:40 +0100 Subject: [PATCH 295/310] Fix error: 'BLEAddress' does not name a type When: compiling a project. Where: Line 54 in: BLEAddress getRemoteAddress(); Drops error: 'BLEAddress' does not name a type My Solution: Add the include: #include "BLEAddress.h" --- cpp_utils/BLERemoteCharacteristic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 47517fff..b8764f65 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -17,6 +17,7 @@ #include "BLERemoteService.h" #include "BLERemoteDescriptor.h" #include "BLEUUID.h" +#include "BLEAddress.h" #include "FreeRTOS.h" class BLERemoteService; From 89aac0a853e29eb29fd80d2d1a7bac606d08d896 Mon Sep 17 00:00:00 2001 From: Ryotaro Onuki Date: Wed, 19 Dec 2018 18:51:35 +0900 Subject: [PATCH 296/310] fix #654 --- cpp_utils/BLERemoteCharacteristic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index db52cdc5..927431aa 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -44,6 +44,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( m_charProp = charProp; m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; + m_rawData = nullptr; retrieveDescriptors(); // Get the descriptors for this characteristic ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); @@ -54,6 +55,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( *@brief Destructor. */ BLERemoteCharacteristic::~BLERemoteCharacteristic() { + if(m_rawData != nullptr) free(m_rawData); removeDescriptors(); // Release resources for any descriptor information we may have allocated. } // ~BLERemoteCharacteristic From 2f13d7483447f53d13fbf2f7aa573f33221b9b3d Mon Sep 17 00:00:00 2001 From: Bill Den Beste Date: Thu, 20 Dec 2018 18:24:05 -0800 Subject: [PATCH 297/310] init should not set I2C addr to 0 init takes sdaPin and clkPin but does not take an I2C address. Therefore the call to init should not set the I2C address to 0. It should leave it alone. --- cpp_utils/PCF8574.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp_utils/PCF8574.cpp b/cpp_utils/PCF8574.cpp index 79be44c0..d246b7a6 100644 --- a/cpp_utils/PCF8574.cpp +++ b/cpp_utils/PCF8574.cpp @@ -116,5 +116,6 @@ void PCF8574::setInvert(bool value) { * @param [in] clkPin The pin to use for the %I2C CLK functions. */ void PCF8574::init(gpio_num_t sdaPin, gpio_num_t clkPin) { - i2c->init(0, sdaPin, clkPin); + uint8_t addr = i2c->getAddress(); + i2c->init(addr, sdaPin, clkPin); } // init From 20c090064e830f661ac3355a0099a511184aff95 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Mon, 24 Dec 2018 12:31:21 +0000 Subject: [PATCH 298/310] Add CMakeLists.txt to allow use with ESP-IDF CMake preview --- cpp_utils/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 cpp_utils/CMakeLists.txt diff --git a/cpp_utils/CMakeLists.txt b/cpp_utils/CMakeLists.txt new file mode 100644 index 00000000..fab2ea80 --- /dev/null +++ b/cpp_utils/CMakeLists.txt @@ -0,0 +1,7 @@ +# Edit following two lines to set component requirements (see docs) +set(COMPONENT_REQUIRES ) +set(COMPONENT_PRIV_REQUIRES ) + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() From b3670f10af98163b468dc1068edffdff8c719792 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 25 Dec 2018 10:48:21 +0000 Subject: [PATCH 299/310] Fix for greedy file(GLOB) --- cpp_utils/CMakeLists.txt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cpp_utils/CMakeLists.txt b/cpp_utils/CMakeLists.txt index fab2ea80..cb9b7267 100644 --- a/cpp_utils/CMakeLists.txt +++ b/cpp_utils/CMakeLists.txt @@ -1,7 +1,20 @@ # Edit following two lines to set component requirements (see docs) -set(COMPONENT_REQUIRES ) +set(COMPONENT_REQUIRES + "console" + "fatfs" + "json" + "mdns" + "nvs_flash" +) set(COMPONENT_PRIV_REQUIRES ) +file(GLOB COMPONENT_SRCS + LIST_DIRECTORIES false + "*.h" + "*.cpp" + "*.c" + "*.S" +) set(COMPONENT_ADD_INCLUDEDIRS ".") register_component() From ea3154a3a5abddd935b2b0a83532e66968f2d56d Mon Sep 17 00:00:00 2001 From: wakwak_koba <5591750+wakwak-koba@users.noreply.github.com> Date: Thu, 17 Jan 2019 21:35:18 +0900 Subject: [PATCH 300/310] fixed bugs --- cpp_utils/BLEDevice.cpp | 18 +++++++++++--- cpp_utils/BLERemoteCharacteristic.cpp | 14 +++++++---- cpp_utils/BLERemoteCharacteristic.h | 2 +- cpp_utils/BLERemoteService.cpp | 34 ++++++++++++++++++++------- cpp_utils/BLEUtils.cpp | 20 ---------------- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 33efabdb..360f97e3 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -162,10 +162,22 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; break; } // switch for(auto &myPair : BLEDevice::getPeerDevices(true)) { - conn_status_t conn_status = (conn_status_t)myPair.second; - if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){ - ((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param); + BLEClient* client = (BLEClient*)((conn_status_t)myPair.second).peer_device; + bool raiseEvent = false; + switch(event) { + case ESP_GATTC_REG_EVT: + raiseEvent = (myPair.first == param->reg.app_id); + break; + case ESP_GATTC_DISCONNECT_EVT: + raiseEvent = (client->getConnId() == param->disconnect.conn_id && client->getGattcIf() == gattc_if); + break; + default: + raiseEvent = (client->getGattcIf() == gattc_if || client->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE); + break; } + + if(raiseEvent) + client->gattClientEventHandler(event, gattc_if, param); } if(m_customGattcHandler != nullptr) { diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index 927431aa..aa49b385 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -149,6 +149,10 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { * @returns N/A */ void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + switch(event) { // ESP_GATTC_NOTIFY_EVT // @@ -262,7 +266,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { uint16_t offset = 0; esp_gattc_descr_elem_t result; while(true) { - uint16_t count = 10; + uint16_t count = 1; esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( getRemoteService()->getClient()->getGattcIf(), getRemoteService()->getClient()->getConnId(), @@ -272,7 +276,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { offset ); - if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { // We have reached the end of the entries. break; } @@ -461,7 +465,8 @@ void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, uint8_t val[] = {0x01, 0x00}; if(!notifications) val[0] = 0x02; BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902)); - desc->writeValue(val, 2); + if(desc != nullptr) + desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( @@ -476,7 +481,8 @@ void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, uint8_t val[] = {0x00, 0x00}; BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902); - desc->writeValue(val, 2); + if(desc != nullptr) + desc->writeValue(val, 2); } // End Unregister m_semaphoreRegForNotifyEvt.wait("registerForNotify"); diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index b8764f65..32305abf 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -53,6 +53,7 @@ class BLERemoteCharacteristic { std::string toString(); uint8_t* readRawData(); BLEAddress getRemoteAddress(); + BLERemoteService* getRemoteService(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); @@ -63,7 +64,6 @@ class BLERemoteCharacteristic { // Private member functions void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - BLERemoteService* getRemoteService(); void removeDescriptors(); void retrieveDescriptors(); diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index ef349986..3b4f2a67 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -61,6 +61,10 @@ void BLERemoteService::gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + switch (event) { // // ESP_GATTC_GET_CHAR_EVT @@ -162,14 +166,14 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { * @return N/A */ void BLERemoteService::retrieveCharacteristics() { - ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); + ESP_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); removeCharacteristics(); // Forget any previous characteristics. uint16_t offset = 0; esp_gattc_char_elem_t result; while (true) { - uint16_t count = 10; // this value is used as in parameter that allows to search max 10 chars with the same uuid + uint16_t count = 1; esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( getClient()->getGattcIf(), getClient()->getConnId(), @@ -180,7 +184,7 @@ void BLERemoteService::retrieveCharacteristics() { offset ); - if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { // We have reached the end of the entries. break; } @@ -209,8 +213,8 @@ void BLERemoteService::retrieveCharacteristics() { } // Loop forever (until we break inside the loop). m_haveCharacteristics = true; // Remember that we have received the characteristics. - ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); -} // getCharacteristics + ESP_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); +} // retrieveCharacteristics /** @@ -229,6 +233,22 @@ std::map* BLERemoteService::getCharacteri return &m_characteristicMap; } // getCharacteristics +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* BLERemoteService::getCharacteristicsByHandle() { + ESP_LOGD(LOG_TAG, ">> getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str()); + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + ESP_LOGD(LOG_TAG, "<< getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str()); + return &m_characteristicMapByHandle; +} // getCharacteristicsByHandle + /** * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID */ @@ -292,10 +312,6 @@ std::string BLERemoteService::getValue(BLEUUID characteristicUuid) { * @return N/A. */ void BLERemoteService::removeCharacteristics() { - for (auto &myPair : m_characteristicMap) { - delete myPair.second; - //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear - } m_characteristicMap.clear(); // Clear the map for (auto &myPair : m_characteristicMapByHandle) { delete myPair.second; diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index 02cdf9ae..a9b735df 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -637,7 +637,6 @@ static std::string gattIdToString(esp_gatt_id_t gattId) { */ 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: @@ -646,7 +645,6 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { 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"; } @@ -689,7 +687,6 @@ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { */ 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 @@ -740,7 +737,6 @@ const char* BLEUtils::advTypeToString(uint8_t advType) { return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; -#endif default: ESP_LOGV(LOG_TAG, " adv data type: 0x%x", advType); return ""; @@ -824,7 +820,6 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { */ 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"; } @@ -852,7 +847,6 @@ std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { case ESP_GATT_CONN_NONE: { return "ESP_GATT_CONN_NONE"; } -#endif default: { return "Unknown"; } @@ -862,7 +856,6 @@ std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { 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: @@ -945,7 +938,6 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType return "ESP_GATTC_WRITE_CHAR_EVT"; case ESP_GATTC_WRITE_DESCR_EVT: return "ESP_GATTC_WRITE_DESCR_EVT"; -#endif default: ESP_LOGV(LOG_TAG, "Unknown GATT Client event type: %d", eventType); return "Unknown"; @@ -960,7 +952,6 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType */ 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: @@ -1011,7 +1002,6 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType 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"; } @@ -1025,14 +1015,12 @@ std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType */ 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"; } @@ -1729,7 +1717,6 @@ void BLEUtils::dumpGattServerEvent( */ 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: @@ -1740,7 +1727,6 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { return "ESP_BLE_EVT_NON_CONN_ADV"; case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; -#endif default: ESP_LOGV(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; @@ -1756,7 +1742,6 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { */ 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: @@ -1811,7 +1796,6 @@ const char* BLEUtils::gapEventToString(uint32_t eventType) { 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: ESP_LOGV(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; @@ -1892,7 +1876,6 @@ std::string BLEUtils::gattServiceToString(uint32_t serviceId) { */ 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: @@ -1979,7 +1962,6 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { return "ESP_GATT_PRC_IN_PROGRESS"; case ESP_GATT_OUT_OF_RANGE: return "ESP_GATT_OUT_OF_RANGE"; -#endif default: return "Unknown"; } @@ -2006,7 +1988,6 @@ std::string BLEUtils::getMember(uint32_t memberId) { */ 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: @@ -2021,7 +2002,6 @@ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { 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: ESP_LOGV(LOG_TAG, "Unknown event type: 0x%x", searchEvt); return "Unknown event type"; From e63c941d11ac827e4378a3c2a08366940d645377 Mon Sep 17 00:00:00 2001 From: wakwak_koba <5591750+wakwak-koba@users.noreply.github.com> Date: Fri, 18 Jan 2019 08:25:09 +0900 Subject: [PATCH 301/310] fixed conversation https://github.com/nkolban/esp32-snippets/pull/783#discussion_r248701585 https://github.com/nkolban/esp32-snippets/pull/783#discussion_r248702002 --- cpp_utils/BLEClient.cpp | 33 ++++++++++++++++++++------------- cpp_utils/BLEDevice.cpp | 18 +++--------------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 8009f408..9dba8469 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -182,19 +182,22 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - // If we receive a disconnect event, set the class flag that indicates that we are - // no longer connected. - m_isConnected = false; - if (m_pClientCallbacks != nullptr) { - m_pClientCallbacks->onDisconnect(this); - } - BLEDevice::removePeerDevice(m_appId, true); - esp_ble_gattc_app_unregister(m_gattc_if); - m_semaphoreRssiCmplEvt.give(); - m_semaphoreSearchCmplEvt.give(1); + ESP_LOGE(__func__, "disconnect event, conn_id: %d", evtParam->disconnect.conn_id); + if(getConnId() != evtParam->disconnect.conn_id) break; + m_semaphoreOpenEvt.give(evtParam->disconnect.reason); + if(!m_isConnected) + break; + // If we receive a disconnect event, set the class flag that indicates that we are + // no longer connected. + esp_ble_gattc_close(m_gattc_if, m_conn_id); + m_isConnected = false; + if (m_pClientCallbacks != nullptr) { + m_pClientCallbacks->onDisconnect(this); + } + break; } // ESP_GATTC_DISCONNECT_EVT - + // // ESP_GATTC_OPEN_EVT // @@ -224,8 +227,12 @@ void BLEClient::gattClientEventHandler( // uint16_t app_id // case ESP_GATTC_REG_EVT: { - m_gattc_if = gattc_if; - m_semaphoreRegEvt.give(); + if(m_appId == evtParam->reg.app_id){ + ESP_LOGI(__func__, "register app id: %d, %d, gattc_if: %d", m_appId, evtParam->reg.app_id, gattc_if); + m_gattc_if = gattc_if; + m_appId = evtParam->reg.app_id; + m_semaphoreRegEvt.give(); + } break; } // ESP_GATTC_REG_EVT diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 360f97e3..33efabdb 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -162,22 +162,10 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; break; } // switch for(auto &myPair : BLEDevice::getPeerDevices(true)) { - BLEClient* client = (BLEClient*)((conn_status_t)myPair.second).peer_device; - bool raiseEvent = false; - switch(event) { - case ESP_GATTC_REG_EVT: - raiseEvent = (myPair.first == param->reg.app_id); - break; - case ESP_GATTC_DISCONNECT_EVT: - raiseEvent = (client->getConnId() == param->disconnect.conn_id && client->getGattcIf() == gattc_if); - break; - default: - raiseEvent = (client->getGattcIf() == gattc_if || client->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE); - break; + conn_status_t conn_status = (conn_status_t)myPair.second; + if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){ + ((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param); } - - if(raiseEvent) - client->gattClientEventHandler(event, gattc_if, param); } if(m_customGattcHandler != nullptr) { From e6d9ff2b9ec8adbccd7d629e85fb419c9d56a9e4 Mon Sep 17 00:00:00 2001 From: chegewara Date: Tue, 22 Jan 2019 10:36:43 +0100 Subject: [PATCH 302/310] Bugfixes --- cpp_utils/BLEAdvertisedDevice.cpp | 11 ++-- cpp_utils/BLEAdvertisedDevice.h | 3 +- cpp_utils/BLEAdvertising.cpp | 8 +-- cpp_utils/BLEBeacon.cpp | 9 +++- cpp_utils/BLECharacteristic.cpp | 9 ++-- cpp_utils/BLEClient.cpp | 77 ++++++++++++++++----------- cpp_utils/BLEClient.h | 6 +-- cpp_utils/BLEDescriptor.cpp | 9 ++-- cpp_utils/BLEDevice.cpp | 20 ++++--- cpp_utils/BLEDevice.h | 4 +- cpp_utils/BLERemoteCharacteristic.cpp | 42 ++++++--------- cpp_utils/BLERemoteCharacteristic.h | 10 ++-- cpp_utils/BLERemoteDescriptor.cpp | 9 ++-- cpp_utils/BLERemoteService.cpp | 63 +++------------------- cpp_utils/BLEScan.cpp | 10 ++-- cpp_utils/BLEServer.cpp | 9 ++-- cpp_utils/BLEService.cpp | 8 +-- cpp_utils/BLEUUID.cpp | 9 ++-- cpp_utils/BLEValue.cpp | 12 +++-- cpp_utils/FreeRTOS.cpp | 8 ++- cpp_utils/GeneralUtils.cpp | 8 ++- 21 files changed, 172 insertions(+), 172 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.cpp b/cpp_utils/BLEAdvertisedDevice.cpp index 41d5faba..d3e60bdd 100644 --- a/cpp_utils/BLEAdvertisedDevice.cpp +++ b/cpp_utils/BLEAdvertisedDevice.cpp @@ -13,14 +13,16 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include #include #include "BLEAdvertisedDevice.h" #include "BLEUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" -#endif +#define LOG_TAG "" +#else +#include "esp_log.h" static const char* LOG_TAG="BLEAdvertisedDevice"; +#endif BLEAdvertisedDevice::BLEAdvertisedDevice() { m_adFlag = 0; @@ -523,5 +525,8 @@ size_t BLEAdvertisedDevice::getPayloadLength() { return m_payloadLength; } +void BLEAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice dev) {} +void BLEAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice* dev) {} + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index aec83746..829f67fe 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -116,7 +116,8 @@ class BLEAdvertisedDeviceCallbacks { * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the * device that was found. During any individual scan, a device will only be detected one time. */ - virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0; + virtual void onResult(BLEAdvertisedDevice advertisedDevice); + virtual void onResult(BLEAdvertisedDevice* advertisedDevice); }; #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp index 41ad87a2..230d77cb 100644 --- a/cpp_utils/BLEAdvertising.cpp +++ b/cpp_utils/BLEAdvertising.cpp @@ -19,16 +19,18 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include "BLEAdvertising.h" -#include #include #include "BLEUtils.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEAdvertising"; #endif -static const char* LOG_TAG = "BLEAdvertising"; /** diff --git a/cpp_utils/BLEBeacon.cpp b/cpp_utils/BLEBeacon.cpp index 854b661d..68f8d8ed 100644 --- a/cpp_utils/BLEBeacon.cpp +++ b/cpp_utils/BLEBeacon.cpp @@ -7,12 +7,17 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include -#include #include "BLEBeacon.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEBeacon"; +#endif #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) -static const char LOG_TAG[] = "BLEBeacon"; BLEBeacon::BLEBeacon() { m_beaconData.manufacturerId = 0x4c00; diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index dee4454a..e3402874 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -11,7 +11,6 @@ #include #include #include "sdkconfig.h" -#include #include #include "BLECharacteristic.h" #include "BLEService.h" @@ -19,11 +18,13 @@ #include "BLEUtils.h" #include "BLE2902.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" -#endif - +#define LOG_TAG "" +#else +#include "esp_log.h" static const char* LOG_TAG = "BLECharacteristic"; +#endif #define NULL_HANDLE (0xffff) diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 9dba8469..32069a3f 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -6,7 +6,6 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include #include #include #include @@ -19,10 +18,15 @@ #include #include #include "BLEDevice.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEClient"; #endif + /* * Design * ------ @@ -43,7 +47,6 @@ * * */ -static const char* LOG_TAG = "BLEClient"; BLEClient::BLEClient() { m_pClientCallbacks = nullptr; @@ -51,6 +54,21 @@ BLEClient::BLEClient() { m_gattc_if = ESP_GATT_IF_NONE; m_haveServices = false; m_isConnected = false; // Initially, we are flagged as not connected. + + + m_appId = BLEDevice::m_appId++; + m_appId = m_appId%100; + BLEDevice::addPeerDevice(this, true, m_appId); + m_semaphoreRegEvt.take("connect"); + + esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreRegEvt.wait("connect"); + } // BLEClient @@ -60,10 +78,10 @@ BLEClient::BLEClient() { BLEClient::~BLEClient() { // We may have allocated service references associated with this client. Before we are finished // with the client, we must release resources. - for (auto &myPair : m_servicesMap) { - delete myPair.second; - } - m_servicesMap.clear(); + clearServices(); + esp_ble_gattc_app_unregister(m_gattc_if); + BLEDevice::removePeerDevice(m_appId, true); + } // ~BLEClient @@ -78,6 +96,7 @@ void BLEClient::clearServices() { delete myPair.second; } m_servicesMap.clear(); + m_servicesMapByInstID.clear(); m_haveServices = false; ESP_LOGD(LOG_TAG, "<< clearServices"); } // clearServices @@ -99,26 +118,12 @@ bool BLEClient::connect(BLEAdvertisedDevice* device) { bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); -// We need the connection handle that we get from registering the application. We register the app -// and then block on its completion. When the event has arrived, we will have the handle. - m_appId = BLEDevice::m_appId++; - BLEDevice::addPeerDevice(this, true, m_appId); - m_semaphoreRegEvt.take("connect"); - - // clearServices(); // we dont need to delete services since every client is unique? - esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return false; - } - - m_semaphoreRegEvt.wait("connect"); - + clearServices(); m_peerAddress = address; // Perform the open connection request against the target BLE Server. m_semaphoreOpenEvt.take("connect"); - errRc = ::esp_ble_gattc_open( + esp_err_t errRc = ::esp_ble_gattc_open( m_gattc_if, *getPeerAddress().getNative(), // address type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. @@ -141,6 +146,7 @@ bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { */ void BLEClient::disconnect() { ESP_LOGD(LOG_TAG, ">> disconnect()"); + // ESP_LOGW(__func__, "gattIf: %d, connId: %d", getGattcIf(), getConnId()); esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId()); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -165,12 +171,13 @@ void BLEClient::gattClientEventHandler( switch(event) { case ESP_GATTC_SRVC_CHG_EVT: + if(getConnId() != evtParam->search_res.conn_id) + break; + ESP_LOGI(LOG_TAG, "SERVICE CHANGED"); break; case ESP_GATTC_CLOSE_EVT: { - // esp_ble_gattc_app_unregister(m_appId); - // BLEDevice::removePeerDevice(m_gattc_if, true); break; } @@ -197,7 +204,7 @@ void BLEClient::gattClientEventHandler( } break; } // ESP_GATTC_DISCONNECT_EVT - + // // ESP_GATTC_OPEN_EVT // @@ -207,12 +214,15 @@ void BLEClient::gattClientEventHandler( // - esp_bd_addr_t remote_bda // case ESP_GATTC_OPEN_EVT: { + if(getConnId() != ESP_GATT_IF_NONE) + break; m_conn_id = evtParam->open.conn_id; if (m_pClientCallbacks != nullptr) { m_pClientCallbacks->onConnect(this); } if (evtParam->open.status == ESP_GATT_OK) { m_isConnected = true; // Flag us as connected. + m_mtu = evtParam->open.mtu; } m_semaphoreOpenEvt.give(evtParam->open.status); break; @@ -240,10 +250,13 @@ void BLEClient::gattClientEventHandler( if(evtParam->cfg_mtu.status != ESP_GATT_OK) { ESP_LOGE(LOG_TAG,"Config mtu failed"); } - m_mtu = evtParam->cfg_mtu.mtu; + else + m_mtu = evtParam->cfg_mtu.mtu; break; case ESP_GATTC_CONNECT_EVT: { + if(evtParam->connect.conn_id != getConnId()) + break; BLEDevice::updatePeerDevice(this, true, m_gattc_if); esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id); if (errRc != ESP_OK) { @@ -265,10 +278,10 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // case ESP_GATTC_SEARCH_CMPL_EVT: { - esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam; - if (p_data->search_cmpl.status != ESP_GATT_OK){ - ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status); + if(evtParam->search_cmpl.conn_id != getConnId()) break; + if (evtParam->search_cmpl.status != ESP_GATT_OK){ + ESP_LOGE(LOG_TAG, "search service failed, error status = %x", evtParam->search_cmpl.status); } #ifndef ARDUINO_ARCH_ESP32 // commented out just for now to keep backward compatibility @@ -280,7 +293,7 @@ void BLEClient::gattClientEventHandler( // ESP_LOGI(LOG_TAG, "unknown service source"); // } #endif - m_semaphoreSearchCmplEvt.give(0); + m_semaphoreSearchCmplEvt.give(evtParam->search_cmpl.status); break; } // ESP_GATTC_SEARCH_CMPL_EVT @@ -295,6 +308,8 @@ void BLEClient::gattClientEventHandler( // - esp_gatt_id_t srvc_id // case ESP_GATTC_SEARCH_RES_EVT: { + if(getConnId() != evtParam->search_res.conn_id) + break; BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id); BLERemoteService* pRemoteService = new BLERemoteService( evtParam->search_res.srvc_id, diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 1b8144d5..2bf352d4 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -65,14 +65,10 @@ uint16_t m_appId; friend class BLERemoteCharacteristic; friend class BLERemoteDescriptor; - void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* param); + void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. uint16_t m_conn_id; -// int m_deviceType; esp_gatt_if_t m_gattc_if; bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. bool m_isConnected = false; // Are we currently connected. diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 4d7b5efd..ba5753de 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -11,16 +11,19 @@ #include #include #include "sdkconfig.h" -#include #include #include "BLEService.h" #include "BLEDescriptor.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEDescriptor"; #endif -static const char* LOG_TAG = "BLEDescriptor"; + #define NULL_HANDLE (0xffff) diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 33efabdb..af401fde 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -19,7 +19,6 @@ #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 ESP-IDF -#include // ESP32 ESP-IDF #include // Part of C++ Standard library #include // Part of C++ Standard library #include // Part of C++ Standard library @@ -28,19 +27,24 @@ #include "BLEClient.h" #include "BLEUtils.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" -#include "esp32-hal-bt.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEDevice"; #endif -static const char* LOG_TAG = "BLEDevice"; +#if defined(ARDUINO_ARCH_ESP32) +#include "esp32-hal-bt.h" +#endif /** * Singletons for the BLEDevice. */ -BLEServer* BLEDevice::m_pServer = nullptr; +// BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; -BLEClient* BLEDevice::m_pClient = nullptr; +// BLEClient* BLEDevice::m_pClient = nullptr; bool initialized = false; esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; @@ -62,7 +66,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); abort(); #endif // CONFIG_GATTC_ENABLE - m_pClient = new BLEClient(); + BLEClient* m_pClient = new BLEClient(); ESP_LOGD(LOG_TAG, "<< createClient"); return m_pClient; } // createClient @@ -78,7 +82,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); abort(); #endif // CONFIG_GATTS_ENABLE - m_pServer = new BLEServer(); + BLEServer* m_pServer = new BLEServer(); m_pServer->createApp(m_appId++); ESP_LOGD(LOG_TAG, "<< createServer"); return m_pServer; diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index e9cd40a3..8f74dc35 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -65,9 +65,9 @@ class BLEDevice { static esp_ble_sec_act_t m_securityLevel; private: - static BLEServer* m_pServer; + // static BLEServer* m_pServer; static BLEScan* m_pScan; - static BLEClient* m_pClient; + // static BLEClient* m_pClient; static BLESecurityCallbacks* m_securityCallbacks; static BLEAdvertising* m_bleAdvertising; static esp_gatt_if_t getGattcIF(); diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index aa49b385..c032ed29 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -11,7 +11,6 @@ #if defined(CONFIG_BT_ENABLED) #include -#include #include #include @@ -19,12 +18,15 @@ #include "BLEUtils.h" #include "GeneralUtils.h" #include "BLERemoteDescriptor.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. #endif -static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. /** * @brief Constructor. @@ -44,7 +46,6 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( m_charProp = charProp; m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; - m_rawData = nullptr; retrieveDescriptors(); // Get the descriptors for this characteristic ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); @@ -55,7 +56,6 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( *@brief Destructor. */ BLERemoteCharacteristic::~BLERemoteCharacteristic() { - if(m_rawData != nullptr) free(m_rawData); removeDescriptors(); // Release resources for any descriptor information we may have allocated. } // ~BLERemoteCharacteristic @@ -149,10 +149,6 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { * @returns N/A */ void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { - - ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", - gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); - switch(event) { // ESP_GATTC_NOTIFY_EVT // @@ -525,10 +521,10 @@ std::string BLERemoteCharacteristic::toString() { * @brief Write the new value for the characteristic. * @param [in] newValue The new value to write. * @param [in] response Do we expect a response? - * @return N/A. + * @return false if not connected or cant perform write for some reason. */ -void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { - writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); +bool BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { + return writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); } // writeValue @@ -538,10 +534,10 @@ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { * This is a convenience function. Many BLE characteristics are a single byte of data. * @param [in] newValue The new byte value to write. * @param [in] response Whether we require a response from the write. - * @return N/A. + * @return false if not connected or cant perform write for some reason. */ -void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { - writeValue(&newValue, 1, response); +bool BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { + return writeValue(&newValue, 1, response); } // writeValue @@ -550,15 +546,16 @@ void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { * @param [in] data A pointer to a data buffer. * @param [in] length The length of the data in the data buffer. * @param [in] response Whether we require a response from the write. + * @return false if not connected or cant perform write for some reason. */ -void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { +bool BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { // writeValue(std::string((char*)data, length), response); ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); // Check to see that we are connected. if (!getRemoteService()->getClient()->isConnected()) { ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); + return false; } m_semaphoreWriteCharEvt.take("writeValue"); @@ -575,12 +572,13 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + return false; } m_semaphoreWriteCharEvt.wait("writeValue"); ESP_LOGD(LOG_TAG, "<< writeValue"); + return true; } // writeValue /** @@ -590,13 +588,5 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp uint8_t* BLERemoteCharacteristic::readRawData() { return m_rawData; } -/** - * @brief Get the address of the remote server - * @return RemoteAddress - */ -BLEAddress BLERemoteCharacteristic::getRemoteAddress(){ - return m_pRemoteService->getClient()->getPeerAddress(); -} - #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 32305abf..ab90c75a 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -17,7 +17,6 @@ #include "BLERemoteService.h" #include "BLERemoteDescriptor.h" #include "BLEUUID.h" -#include "BLEAddress.h" #include "FreeRTOS.h" class BLERemoteService; @@ -47,13 +46,11 @@ class BLERemoteCharacteristic { uint16_t readUInt16(); uint32_t readUInt32(); void registerForNotify(notify_callback _callback, bool notifications = true); - void writeValue(uint8_t* data, size_t length, bool response = false); - void writeValue(std::string newValue, bool response = false); - void writeValue(uint8_t newValue, bool response = false); + bool writeValue(uint8_t* data, size_t length, bool response = false); + bool writeValue(std::string newValue, bool response = false); + bool writeValue(uint8_t newValue, bool response = false); std::string toString(); uint8_t* readRawData(); - BLEAddress getRemoteAddress(); - BLERemoteService* getRemoteService(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); @@ -64,6 +61,7 @@ class BLERemoteCharacteristic { // Private member functions void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); + BLERemoteService* getRemoteService(); void removeDescriptors(); void retrieveDescriptors(); diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index ea7eeebd..96a8a577 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -9,12 +9,15 @@ #include #include "BLERemoteDescriptor.h" #include "GeneralUtils.h" -#include -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteDescriptor"; #endif -static const char* LOG_TAG = "BLERemoteDescriptor"; + BLERemoteDescriptor::BLERemoteDescriptor( diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 3b4f2a67..91089eab 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -11,13 +11,16 @@ #include "BLERemoteService.h" #include "BLEUtils.h" #include "GeneralUtils.h" -#include #include -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteService"; #endif -static const char* LOG_TAG = "BLERemoteService"; + BLERemoteService::BLERemoteService( esp_gatt_id_t srvcId, @@ -61,57 +64,7 @@ void BLERemoteService::gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { - - ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", - gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); - switch (event) { - // - // 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: { - // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be - // the same. - if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) { - break; - } - - // If the status is NOT OK then we have a problem and continue. - if (evtParam->get_char.status != ESP_GATT_OK) { - m_semaphoreGetCharEvt.give(); - break; - } - - // This is an indication that we now have the characteristic details for a characteristic owned - // by this service so remember it. - m_characteristicMap.insert(std::pair( - BLEUUID(evtParam->get_char.char_id.uuid).toString(), - new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) )); - - - // Now that we have received a characteristic, lets ask for the next one. - esp_err_t errRc = ::esp_ble_gattc_get_characteristic( - m_pClient->getGattcIf(), - m_pClient->getConnId(), - &m_srvcId, - &evtParam->get_char.char_id); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - break; - } - - //m_semaphoreGetCharEvt.give(); - break; - } // ESP_GATTC_GET_CHAR_EVT -*/ default: break; } // switch @@ -173,7 +126,7 @@ void BLERemoteService::retrieveCharacteristics() { uint16_t offset = 0; esp_gattc_char_elem_t result; while (true) { - uint16_t count = 1; + uint16_t count = 1; // this value is used as in parameter that allows to search max 10 chars with the same uuid esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( getClient()->getGattcIf(), getClient()->getConnId(), @@ -254,7 +207,7 @@ std::map* BLERemoteService::getCharacteristi */ void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" - pCharacteristicMap = &m_characteristicMapByHandle; + *pCharacteristicMap = m_characteristicMapByHandle; } // Get the characteristics map. /** diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index e1778b84..a96e6df6 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -8,7 +8,6 @@ #if defined(CONFIG_BT_ENABLED) -#include #include #include @@ -17,11 +16,15 @@ #include "BLEScan.h" #include "BLEUtils.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEScan"; #endif -static const char* LOG_TAG = "BLEScan"; + /** @@ -122,6 +125,7 @@ void BLEScan::handleGAPEvent( } if (m_pAdvertisedDeviceCallbacks) { + m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } if(found) diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 9b52762d..6a780aa9 100755 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -7,7 +7,6 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include #include #include #include "GeneralUtils.h" @@ -18,11 +17,15 @@ #include #include #include -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEServer"; #endif -static const char* LOG_TAG = "BLEServer"; + /** diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 7e7d67ac..3034cf18 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -11,7 +11,6 @@ #if defined(CONFIG_BT_ENABLED) #include #include -#include #include #include @@ -22,13 +21,16 @@ #include "BLEUtils.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEService"; // Tag for logging. #endif #define NULL_HANDLE (0xffff) -static const char* LOG_TAG = "BLEService"; // Tag for logging. /** * @brief Construct an instance of the BLEService diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index fd0f5ad5..4ddf8fc2 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -6,7 +6,6 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include #include #include #include @@ -14,12 +13,16 @@ #include #include #include "BLEUUID.h" -static const char* LOG_TAG = "BLEUUID"; -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEUUID"; #endif + /** * @brief Copy memory from source to target but in reverse order. * diff --git a/cpp_utils/BLEValue.cpp b/cpp_utils/BLEValue.cpp index aac0f500..ec1e61f5 100644 --- a/cpp_utils/BLEValue.cpp +++ b/cpp_utils/BLEValue.cpp @@ -6,15 +6,17 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) - -#include - #include "BLEValue.h" -#ifdef ARDUINO_ARCH_ESP32 + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG="BLEValue"; #endif -static const char* LOG_TAG="BLEValue"; + BLEValue::BLEValue() { m_accumulation = ""; diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 9514eed4..40e5bbf3 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -11,10 +11,14 @@ #include #include #include "FreeRTOS.h" -#include #include "sdkconfig.h" - +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" static const char* LOG_TAG = "FreeRTOS"; +#endif /** * Sleep for the specified number of milliseconds. diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index dbbc65be..019c81bd 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -6,7 +6,6 @@ */ #include "GeneralUtils.h" -#include #include #include #include @@ -20,7 +19,14 @@ #include #include +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" static const char* LOG_TAG = "GeneralUtils"; +#endif + static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" From d141a913eb4f26d081a645e9a83b66f534e2661f Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 30 Jan 2019 14:50:59 +0100 Subject: [PATCH 303/310] Bugfix: write long characteristic value Bugfix: memory leak in BLERemoteCharacteristic Bugfix: BLEClient use gattc_if for connections Add: add delete BLEClient callbacks to avoid memory leak Add: add BLEServer disconnect function --- cpp_utils/BLEAdvertisedDevice.h | 2 +- cpp_utils/BLECharacteristic.cpp | 48 ++++++++++++++++----------- cpp_utils/BLECharacteristic.h | 1 + cpp_utils/BLEClient.cpp | 25 ++++++++------ cpp_utils/BLEClient.h | 5 +-- cpp_utils/BLEDevice.cpp | 8 ++--- cpp_utils/BLEDevice.h | 2 +- cpp_utils/BLERemoteCharacteristic.cpp | 1 + cpp_utils/BLERemoteCharacteristic.h | 6 ++-- cpp_utils/BLEServer.cpp | 6 ++++ cpp_utils/BLEServer.h | 1 + 11 files changed, 61 insertions(+), 44 deletions(-) diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h index 829f67fe..cd206d4a 100644 --- a/cpp_utils/BLEAdvertisedDevice.h +++ b/cpp_utils/BLEAdvertisedDevice.h @@ -95,7 +95,7 @@ class BLEAdvertisedDevice { int8_t m_txPower; std::string m_serviceData; BLEUUID m_serviceDataUUID; - uint8_t* m_payload; + uint8_t* m_payload = nullptr; size_t m_payloadLength = 0; esp_ble_addr_type_t m_addressType; }; diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index e3402874..80f9c7e7 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -224,21 +224,24 @@ void BLECharacteristic::handleGATTServerEvent( // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL // case ESP_GATTS_EXEC_WRITE_EVT: { - if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { - m_value.commit(); - if (m_pCallbacks != nullptr) { - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + if(m_writeEvt){ + m_writeEvt = false; + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + m_value.commit(); + if (m_pCallbacks != nullptr) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } else { + m_value.cancel(); + } + // ??? + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, + param->write.conn_id, + param->write.trans_id, ESP_GATT_OK, nullptr); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - } else { - m_value.cancel(); - } -// ??? - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, - param->write.conn_id, - param->write.trans_id, ESP_GATT_OK, nullptr); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } break; } // ESP_GATTS_EXEC_WRITE_EVT @@ -284,7 +287,11 @@ void BLECharacteristic::handleGATTServerEvent( if (param->write.handle == m_handle) { if (param->write.is_prep) { m_value.addPart(param->write.value, param->write.len); + m_writeEvt = true; } else { + if (m_pCallbacks != nullptr && param->write.is_prep != true) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } setValue(param->write.value, param->write.len); } @@ -313,9 +320,6 @@ void BLECharacteristic::handleGATTServerEvent( } } // Response needed - if (m_pCallbacks != nullptr && param->write.is_prep != true) { - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. - } } // Match on handles. break; } // ESP_GATTS_WRITE_EVT @@ -384,6 +388,9 @@ void BLECharacteristic::handleGATTServerEvent( } } else { // read.is_long == false + if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + m_pCallbacks->onRead(this); // Invoke the read callback. + } std::string value = m_value.getValue(); if (value.length() + 1 > maxOffset) { @@ -399,9 +406,9 @@ void BLECharacteristic::handleGATTServerEvent( memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); } - if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback - m_pCallbacks->onRead(this); // Invoke the read callback. - } + // if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + // m_pCallbacks->onRead(this); // Invoke the read callback. + // } } rsp.attr_value.handle = param->read.handle; rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; @@ -530,6 +537,7 @@ void BLECharacteristic::notify(bool is_notification) { if(!is_notification) m_semaphoreConfEvt.wait("indicate"); } + delete(p2902); ESP_LOGD(LOG_TAG, "<< notify"); } // Notify diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h index 5eb1e8d6..82a8d7d0 100644 --- a/cpp_utils/BLECharacteristic.h +++ b/cpp_utils/BLECharacteristic.h @@ -105,6 +105,7 @@ class BLECharacteristic { BLEService* m_pService; BLEValue m_value; esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + bool m_writeEvt = false; void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 32069a3f..d6e75988 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -81,6 +81,8 @@ BLEClient::~BLEClient() { clearServices(); esp_ble_gattc_app_unregister(m_gattc_if); BLEDevice::removePeerDevice(m_appId, true); + if(m_deleteCallbacks) + delete m_pClientCallbacks; } // ~BLEClient @@ -171,15 +173,14 @@ void BLEClient::gattClientEventHandler( switch(event) { case ESP_GATTC_SRVC_CHG_EVT: - if(getConnId() != evtParam->search_res.conn_id) + if(m_gattc_if != gattc_if) break; ESP_LOGI(LOG_TAG, "SERVICE CHANGED"); break; - case ESP_GATTC_CLOSE_EVT: { + case ESP_GATTC_CLOSE_EVT: break; - } // // ESP_GATTC_DISCONNECT_EVT @@ -189,8 +190,8 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGE(__func__, "disconnect event, conn_id: %d", evtParam->disconnect.conn_id); - if(getConnId() != evtParam->disconnect.conn_id) + ESP_LOGE(__func__, "disconnect event, reason: %d, connId: %d, my connId: %d, my IF: %d, gattc_if: %d", (int)evtParam->disconnect.reason, evtParam->disconnect.conn_id, getConnId(), getGattcIf(), gattc_if); + if(m_gattc_if != gattc_if) break; m_semaphoreOpenEvt.give(evtParam->disconnect.reason); if(!m_isConnected) @@ -214,7 +215,7 @@ void BLEClient::gattClientEventHandler( // - esp_bd_addr_t remote_bda // case ESP_GATTC_OPEN_EVT: { - if(getConnId() != ESP_GATT_IF_NONE) + if(m_gattc_if != gattc_if) break; m_conn_id = evtParam->open.conn_id; if (m_pClientCallbacks != nullptr) { @@ -240,7 +241,6 @@ void BLEClient::gattClientEventHandler( if(m_appId == evtParam->reg.app_id){ ESP_LOGI(__func__, "register app id: %d, %d, gattc_if: %d", m_appId, evtParam->reg.app_id, gattc_if); m_gattc_if = gattc_if; - m_appId = evtParam->reg.app_id; m_semaphoreRegEvt.give(); } break; @@ -255,8 +255,9 @@ void BLEClient::gattClientEventHandler( break; case ESP_GATTC_CONNECT_EVT: { - if(evtParam->connect.conn_id != getConnId()) + if(m_gattc_if != gattc_if) break; + m_conn_id = evtParam->connect.conn_id; BLEDevice::updatePeerDevice(this, true, m_gattc_if); esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id); if (errRc != ESP_OK) { @@ -278,7 +279,7 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // case ESP_GATTC_SEARCH_CMPL_EVT: { - if(evtParam->search_cmpl.conn_id != getConnId()) + if(m_gattc_if != gattc_if) break; if (evtParam->search_cmpl.status != ESP_GATT_OK){ ESP_LOGE(LOG_TAG, "search service failed, error status = %x", evtParam->search_cmpl.status); @@ -308,8 +309,9 @@ void BLEClient::gattClientEventHandler( // - esp_gatt_id_t srvc_id // case ESP_GATTC_SEARCH_RES_EVT: { - if(getConnId() != evtParam->search_res.conn_id) + if(m_gattc_if != gattc_if) break; + BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id); BLERemoteService* pRemoteService = new BLERemoteService( evtParam->search_res.srvc_id, @@ -515,8 +517,9 @@ bool BLEClient::isConnected() { /** * @brief Set the callbacks that will be invoked. */ -void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) { +void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { m_pClientCallbacks = pClientCallbacks; + m_deleteCallbacks = deleteCallbacks; } // setClientCallbacks diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index 2bf352d4..4b3c7eea 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -50,7 +50,7 @@ class BLEClient { bool isConnected(); // Return true if we are connected. - void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); + void setClientCallbacks(BLEClientCallbacks *pClientCallbacks, bool deleteCallbacks = true); void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. std::string toString(); // Return a string representation of this client. @@ -72,8 +72,9 @@ uint16_t m_appId; esp_gatt_if_t m_gattc_if; bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. bool m_isConnected = false; // Are we currently connected. + bool m_deleteCallbacks = true; - BLEClientCallbacks* m_pClientCallbacks; + BLEClientCallbacks* m_pClientCallbacks = nullptr; FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index af401fde..78bd13d3 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -42,7 +42,7 @@ static const char* LOG_TAG = "BLEDevice"; /** * Singletons for the BLEDevice. */ -// BLEServer* BLEDevice::m_pServer = nullptr; +BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; // BLEClient* BLEDevice::m_pClient = nullptr; bool initialized = false; @@ -82,7 +82,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); abort(); #endif // CONFIG_GATTS_ENABLE - BLEServer* m_pServer = new BLEServer(); + BLEDevice::m_pServer = new BLEServer(); m_pServer->createApp(m_appId++); ESP_LOGD(LOG_TAG, "<< createServer"); return m_pServer; @@ -266,10 +266,6 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; } } // switch - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->handleGAPEvent(event, param); - } - if (BLEDevice::m_pScan != nullptr) { BLEDevice::getScan()->handleGAPEvent(event, param); } diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 8f74dc35..b01f09be 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -65,7 +65,7 @@ class BLEDevice { static esp_ble_sec_act_t m_securityLevel; private: - // static BLEServer* m_pServer; + static BLEServer* m_pServer; static BLEScan* m_pScan; // static BLEClient* m_pClient; static BLESecurityCallbacks* m_securityCallbacks; diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index c032ed29..ef16da7b 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -57,6 +57,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( */ BLERemoteCharacteristic::~BLERemoteCharacteristic() { removeDescriptors(); // Release resources for any descriptor information we may have allocated. + if(m_rawData != nullptr) free(m_rawData); } // ~BLERemoteCharacteristic diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index ab90c75a..910b11a4 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -51,6 +51,7 @@ class BLERemoteCharacteristic { bool writeValue(uint8_t newValue, bool response = false); std::string toString(); uint8_t* readRawData(); + BLERemoteService* getRemoteService(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); @@ -61,7 +62,6 @@ class BLERemoteCharacteristic { // Private member functions void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - BLERemoteService* getRemoteService(); void removeDescriptors(); void retrieveDescriptors(); @@ -74,8 +74,8 @@ class BLERemoteCharacteristic { FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; - uint8_t *m_rawData; - notify_callback m_notifyCallback; + uint8_t *m_rawData = nullptr; + notify_callback m_notifyCallback = nullptr; // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. std::map m_descriptorMap; diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp index 6a780aa9..83497331 100755 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -9,6 +9,7 @@ #if defined(CONFIG_BT_ENABLED) #include #include +#include #include "GeneralUtils.h" #include "BLEDevice.h" #include "BLEServer.h" @@ -421,4 +422,9 @@ void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms esp_ble_gap_update_conn_params(&conn_params); } + +void BLEServer::disconnect(uint16_t connId){ + esp_ble_gatts_close(m_gatts_if, connId); +} + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h index d39d8bfe..d2f8038d 100755 --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -72,6 +72,7 @@ class BLEServer { BLEService* getServiceByUUID(const char* uuid); BLEService* getServiceByUUID(BLEUUID uuid); bool connect(BLEAddress address); + void disconnect(uint16_t connId); uint16_t m_appId; void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); From 3de1717ba5e1fc6d1c7726b13fa6721c90acc86e Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 30 Jan 2019 15:59:21 +0100 Subject: [PATCH 304/310] Add static pin feature --- cpp_utils/BLESecurity.cpp | 11 +++++++++++ cpp_utils/BLESecurity.h | 1 + 2 files changed, 12 insertions(+) diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp index 921f5424..f3b2cd3c 100644 --- a/cpp_utils/BLESecurity.cpp +++ b/cpp_utils/BLESecurity.cpp @@ -61,6 +61,17 @@ void BLESecurity::setKeySize(uint8_t key_size) { esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); } //setKeySize +/** + * Setup for static PIN connection, call it first and then call setAuthenticationMode eventually to change it + */ +void BLESecurity::setStaticPIN(uint32_t pin){ + uint32_t passkey = pin; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + setCapability(ESP_IO_CAP_OUT); + setKeySize(); + setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +} /** * @brief Debug function to display what keys are exchanged by peers diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h index 48d09d2f..dc6d6d71 100644 --- a/cpp_utils/BLESecurity.h +++ b/cpp_utils/BLESecurity.h @@ -21,6 +21,7 @@ class BLESecurity { void setInitEncryptionKey(uint8_t init_key); void setRespEncryptionKey(uint8_t resp_key); void setKeySize(uint8_t key_size = 16); + void setStaticPIN(uint32_t pin); static char* esp_key_type_to_str(esp_ble_key_type_t key_type); private: From 6005e319e8072f57fd2bc9f786a3042fa23db184 Mon Sep 17 00:00:00 2001 From: chegewara Date: Wed, 30 Jan 2019 16:56:25 +0100 Subject: [PATCH 305/310] Update arduino examples --- .../Arduino/BLE_client/BLE_client.ino | 4 +- .../Arduino/BLE_server/BLE_server.ino | 3 +- .../BLE_client/BLE_client_encrypted.ino | 138 -------------- .../BLE_client_numeric_confirmation.ino | 169 ------------------ .../BLE_client/BLE_client_passkey.ino | 168 ----------------- .../BLE_server_passkey/BLE_server_passkey.ino | 6 +- .../Arduino/security/StaticPIN/StaticPIN.ino | 47 +++++ 7 files changed, 56 insertions(+), 479 deletions(-) delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino delete mode 100644 cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino create mode 100644 cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino index c0b6163c..fb032a69 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino @@ -6,9 +6,9 @@ //#include "BLEScan.h" // The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); // The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); static BLEAddress *pServerAddress; static boolean doConnect = false; diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino index 38224a67..79793975 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino @@ -29,6 +29,7 @@ void setup() { pCharacteristic->setValue("Hello World says Neil"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->start(); Serial.println("Characteristic defined! Now you can read it in your phone!"); } @@ -36,4 +37,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino deleted file mode 100644 index e77d774f..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted.ino +++ /dev/null @@ -1,138 +0,0 @@ -/** - * A BLE client example that is rich in capabilities. - */ - -#include "BLEDevice.h" -//#include "BLEScan.h" - -// The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); -// The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); - -static BLEAddress *pServerAddress; -static boolean doConnect = false; -static boolean connected = false; -static BLERemoteCharacteristic* pRemoteCharacteristic; - -static void notifyCallback( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify) { - Serial.print("Notify callback for characteristic "); - Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); - Serial.print(" of data length "); - Serial.println(length); -} - -bool connectToServer(BLEAddress pAddress) { - Serial.print("Forming a connection to "); - Serial.println(pAddress.toString().c_str()); - - /* - * Here we have implemented simplest security. This kind security does not provide authentication - */ - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - BLEClient* pClient = BLEDevice::createClient(); - Serial.println(" - Created client"); - - // Connect to the remove BLE Server. - pClient->connect(pAddress); - Serial.println(" - Connected to server"); - - // Obtain a reference to the service we are after in the remote BLE server. - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); - if (pRemoteService == nullptr) { - Serial.print("Failed to find our service UUID: "); - Serial.println(serviceUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our service"); - - - // Obtain a reference to the characteristic in the service of the remote BLE server. - pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); - if (pRemoteCharacteristic == nullptr) { - Serial.print("Failed to find our characteristic UUID: "); - Serial.println(charUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our characteristic"); - - // Read the value of the characteristic. - std::string value = pRemoteCharacteristic->readValue(); - Serial.print("The characteristic value was: "); - Serial.println(value.c_str()); - - pRemoteCharacteristic->registerForNotify(notifyCallback); -} -/** - * Scan for BLE servers and find the first one that advertises the service we are looking for. - */ -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - void onResult(BLEAdvertisedDevice advertisedDevice) { - Serial.print("BLE Advertised Device found: "); - Serial.println(advertisedDevice.toString().c_str()); - - // We have found a device, let us now see if it contains the service we are looking for. - if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { - - // - Serial.print("Found our device! address: "); - advertisedDevice.getScan()->stop(); - - pServerAddress = new BLEAddress(advertisedDevice.getAddress()); - doConnect = true; - - } // Found our server - } // onResult -}; // MyAdvertisedDeviceCallbacks - - -void setup() { - Serial.begin(115200); - Serial.println("Starting Arduino BLE Client application..."); - BLEDevice::init(""); - - // Retrieve a Scanner and set the callback we want to use to be informed when we - // have detected a new device. Specify that we want active scanning and start the - // scan to run for 30 seconds. - BLEScan* pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setActiveScan(true); - pBLEScan->start(30); -} // End of setup. - - -// This is the Arduino main loop function. -void loop() { - - // If the flag "doConnect" is true then we have scanned for and found the desired - // BLE Server with which we wish to connect. Now we connect to it. Once we are - // connected we set the connected flag to be true. - if (doConnect == true) { - if (connectToServer(*pServerAddress)) { - Serial.println("We are now connected to the BLE Server."); - connected = true; - } else { - Serial.println("We have failed to connect to the server; there is nothin more we will do."); - } - doConnect = false; - } - - // If we are connected to a peer BLE Server, update the characteristic each time we are reached - // with the current time since boot. - if (connected) { - String newValue = "Time since boot: " + String(millis()/1000); - Serial.println("Setting new characteristic value to \"" + newValue + "\""); - - // Set the characteristic's value to be the array of bytes that is actually a string. - pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); - } - - delay(1000); // Delay a second between loops. -} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino deleted file mode 100644 index 846a664b..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation.ino +++ /dev/null @@ -1,169 +0,0 @@ -/** - * A BLE client example that is rich in capabilities. - */ - -#include "BLEDevice.h" -//#include "BLEScan.h" - -// The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); -// The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); - -static BLEAddress *pServerAddress; -static boolean doConnect = false; -static boolean connected = false; -static BLERemoteCharacteristic* pRemoteCharacteristic; - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onConfirmPIN(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); - vTaskDelay(5000); - return true; - } - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "Security Request"); - return true; - } - void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ - if(auth_cmpl.success){ - ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); - esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); - ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); - } - ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); - } -}; - -static void notifyCallback( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify) { - Serial.print("Notify callback for characteristic "); - Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); - Serial.print(" of data length "); - Serial.println(length); -} - -bool connectToServer(BLEAddress pAddress) { - Serial.print("Forming a connection to "); - Serial.println(pAddress.toString().c_str()); - - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - BLEDevice::setSecurityCallbacks(new MySecurity()); - - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setKeySize(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_IO); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - BLEClient* pClient = BLEDevice::createClient(); - Serial.println(" - Created client"); - - // Connect to the remove BLE Server. - pClient->connect(pAddress); - Serial.println(" - Connected to server"); - - // Obtain a reference to the service we are after in the remote BLE server. - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); - if (pRemoteService == nullptr) { - Serial.print("Failed to find our service UUID: "); - Serial.println(serviceUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our service"); - - - // Obtain a reference to the characteristic in the service of the remote BLE server. - pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); - if (pRemoteCharacteristic == nullptr) { - Serial.print("Failed to find our characteristic UUID: "); - Serial.println(charUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our characteristic"); - - // Read the value of the characteristic. - std::string value = pRemoteCharacteristic->readValue(); - Serial.print("The characteristic value was: "); - Serial.println(value.c_str()); - - pRemoteCharacteristic->registerForNotify(notifyCallback); -} -/** - * Scan for BLE servers and find the first one that advertises the service we are looking for. - */ -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - void onResult(BLEAdvertisedDevice advertisedDevice) { - Serial.print("BLE Advertised Device found: "); - Serial.println(advertisedDevice.toString().c_str()); - - // We have found a device, let us now see if it contains the service we are looking for. - if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { - - // - Serial.print("Found our device! address: "); - advertisedDevice.getScan()->stop(); - - pServerAddress = new BLEAddress(advertisedDevice.getAddress()); - doConnect = true; - - } // Found our server - } // onResult -}; // MyAdvertisedDeviceCallbacks - - -void setup() { - Serial.begin(115200); - Serial.println("Starting Arduino BLE Client application..."); - BLEDevice::init(""); - - // Retrieve a Scanner and set the callback we want to use to be informed when we - // have detected a new device. Specify that we want active scanning and start the - // scan to run for 30 seconds. - BLEScan* pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setActiveScan(true); - pBLEScan->start(30); -} // End of setup. - - -// This is the Arduino main loop function. -void loop() { - - // If the flag "doConnect" is true then we have scanned for and found the desired - // BLE Server with which we wish to connect. Now we connect to it. Once we are - // connected we set the connected flag to be true. - if (doConnect == true) { - if (connectToServer(*pServerAddress)) { - Serial.println("We are now connected to the BLE Server."); - connected = true; - } else { - Serial.println("We have failed to connect to the server; there is nothin more we will do."); - } - doConnect = false; - } - - // If we are connected to a peer BLE Server, update the characteristic each time we are reached - // with the current time since boot. - if (connected) { - String newValue = "Time since boot: " + String(millis()/1000); - Serial.println("Setting new characteristic value to \"" + newValue + "\""); - - // Set the characteristic's value to be the array of bytes that is actually a string. - pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); - } - - delay(1000); // Delay a second between loops. -} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino deleted file mode 100644 index 763a87d2..00000000 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey.ino +++ /dev/null @@ -1,168 +0,0 @@ -/** - * A BLE client example that is rich in capabilities. - */ - -#include "BLEDevice.h" -//#include "BLEScan.h" - -// The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); -// The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); - -static BLEAddress *pServerAddress; -static boolean doConnect = false; -static boolean connected = false; -static BLERemoteCharacteristic* pRemoteCharacteristic; - -class MySecurity : public BLESecurityCallbacks { - - uint32_t onPassKeyRequest(){ - return 123456; - } - void onPassKeyNotify(uint32_t pass_key){ - ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); - } - bool onConfirmPIN(uint32_t pass_key){ - ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); - vTaskDelay(5000); - return true; - } - bool onSecurityRequest(){ - ESP_LOGI(LOG_TAG, "Security Request"); - return true; - } - void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ - if(auth_cmpl.success){ - ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); - esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); - ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); - } - ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); - } -}; - -static void notifyCallback( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify) { - Serial.print("Notify callback for characteristic "); - Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); - Serial.print(" of data length "); - Serial.println(length); -} - -bool connectToServer(BLEAddress pAddress) { - Serial.print("Forming a connection to "); - Serial.println(pAddress.toString().c_str()); - - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - BLEDevice::setSecurityCallbacks(new MySecurity()); - - BLESecurity *pSecurity = new BLESecurity(); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - pSecurity->setCapability(ESP_IO_CAP_OUT); - pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - BLEClient* pClient = BLEDevice::createClient(); - Serial.println(" - Created client"); - - // Connect to the remove BLE Server. - pClient->connect(pAddress); - Serial.println(" - Connected to server"); - - // Obtain a reference to the service we are after in the remote BLE server. - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); - if (pRemoteService == nullptr) { - Serial.print("Failed to find our service UUID: "); - Serial.println(serviceUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our service"); - - - // Obtain a reference to the characteristic in the service of the remote BLE server. - pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); - if (pRemoteCharacteristic == nullptr) { - Serial.print("Failed to find our characteristic UUID: "); - Serial.println(charUUID.toString().c_str()); - return false; - } - Serial.println(" - Found our characteristic"); - - // Read the value of the characteristic. - std::string value = pRemoteCharacteristic->readValue(); - Serial.print("The characteristic value was: "); - Serial.println(value.c_str()); - - pRemoteCharacteristic->registerForNotify(notifyCallback); -} -/** - * Scan for BLE servers and find the first one that advertises the service we are looking for. - */ -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - void onResult(BLEAdvertisedDevice advertisedDevice) { - Serial.print("BLE Advertised Device found: "); - Serial.println(advertisedDevice.toString().c_str()); - - // We have found a device, let us now see if it contains the service we are looking for. - if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) { - - // - Serial.print("Found our device! address: "); - advertisedDevice.getScan()->stop(); - - pServerAddress = new BLEAddress(advertisedDevice.getAddress()); - doConnect = true; - - } // Found our server - } // onResult -}; // MyAdvertisedDeviceCallbacks - - -void setup() { - Serial.begin(115200); - Serial.println("Starting Arduino BLE Client application..."); - BLEDevice::init(""); - - // Retrieve a Scanner and set the callback we want to use to be informed when we - // have detected a new device. Specify that we want active scanning and start the - // scan to run for 30 seconds. - BLEScan* pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setActiveScan(true); - pBLEScan->start(30); -} // End of setup. - - -// This is the Arduino main loop function. -void loop() { - - // If the flag "doConnect" is true then we have scanned for and found the desired - // BLE Server with which we wish to connect. Now we connect to it. Once we are - // connected we set the connected flag to be true. - if (doConnect == true) { - if (connectToServer(*pServerAddress)) { - Serial.println("We are now connected to the BLE Server."); - connected = true; - } else { - Serial.println("We have failed to connect to the server; there is nothin more we will do."); - } - doConnect = false; - } - - // If we are connected to a peer BLE Server, update the characteristic each time we are reached - // with the current time since boot. - if (connected) { - String newValue = "Time since boot: " + String(millis()/1000); - Serial.println("Setting new characteristic value to \"" + newValue + "\""); - - // Set the characteristic's value to be the array of bytes that is actually a string. - pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); - } - - delay(1000); // Delay a second between loops. -} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino index a48ad482..93f8c89f 100644 --- a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino @@ -14,7 +14,11 @@ #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" class MySecurity : public BLESecurityCallbacks { - + + bool onConfirmPIN(uint32_t pin){ + return false; + } + uint32_t onPassKeyRequest(){ ESP_LOGI(LOG_TAG, "PassKeyRequest"); return 123456; diff --git a/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino b/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino new file mode 100644 index 00000000..d89c14f9 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino @@ -0,0 +1,47 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("ESP32"); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setStaticPIN(123456); + + //set static passkey + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} From 8fac333f3bbe1e3567e1f4c0dbb9c778a5910d36 Mon Sep 17 00:00:00 2001 From: chegewara Date: Fri, 8 Feb 2019 09:53:49 +0100 Subject: [PATCH 306/310] Fix missing return --- cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino index fb032a69..7bce73f1 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino @@ -62,6 +62,8 @@ bool connectToServer(BLEAddress pAddress) { Serial.println(value.c_str()); pRemoteCharacteristic->registerForNotify(notifyCallback); + + return true; } /** * Scan for BLE servers and find the first one that advertises the service we are looking for. From 43491c7ae825c8fd1d6b08af4869500693ea6b1c Mon Sep 17 00:00:00 2001 From: chegewara Date: Fri, 8 Feb 2019 09:57:01 +0100 Subject: [PATCH 307/310] Fix #803 --- cpp_utils/BLECharacteristic.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp index 80f9c7e7..2868ee26 100644 --- a/cpp_utils/BLECharacteristic.cpp +++ b/cpp_utils/BLECharacteristic.cpp @@ -289,10 +289,10 @@ void BLECharacteristic::handleGATTServerEvent( m_value.addPart(param->write.value, param->write.len); m_writeEvt = true; } else { + setValue(param->write.value, param->write.len); if (m_pCallbacks != nullptr && param->write.is_prep != true) { m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. } - setValue(param->write.value, param->write.len); } ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", @@ -503,7 +503,11 @@ void BLECharacteristic::notify(bool is_notification) { // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled // and, if not, prevent the notification. - BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + BLE2902* p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(p2902 == nullptr){ + ESP_LOGE(LOG_TAG, "Characteristic without 0x2902 descriptor"); + return; + } if(is_notification) { if (p2902 != nullptr && !p2902->getNotifications()) { ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); @@ -537,7 +541,6 @@ void BLECharacteristic::notify(bool is_notification) { if(!is_notification) m_semaphoreConfEvt.wait("indicate"); } - delete(p2902); ESP_LOGD(LOG_TAG, "<< notify"); } // Notify From 2c256fcf35fc133ca5dab92f23249a9feb63b3bc Mon Sep 17 00:00:00 2001 From: daschiller Date: Tue, 12 Feb 2019 11:01:32 +0100 Subject: [PATCH 308/310] Notify after setBatteryLevel This was discussed in #739, but never implemented. --- cpp_utils/BLEHIDDevice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp index 69e18be7..46770b74 100644 --- a/cpp_utils/BLEHIDDevice.cpp +++ b/cpp_utils/BLEHIDDevice.cpp @@ -194,6 +194,7 @@ BLECharacteristic* BLEHIDDevice::protocolMode() { void BLEHIDDevice::setBatteryLevel(uint8_t level) { m_batteryLevelCharacteristic->setValue(&level, 1); + m_batteryLevelCharacteristic->notify(); } /* * @brief Returns battery level characteristic From e7e26536a2f7b6cd36fc396ecd957116f4131d40 Mon Sep 17 00:00:00 2001 From: h2zero <32826625+h2zero@users.noreply.github.com> Date: Tue, 30 Apr 2019 18:29:05 -0600 Subject: [PATCH 309/310] Update FreeRTOS.cpp --- cpp_utils/FreeRTOS.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp_utils/FreeRTOS.cpp b/cpp_utils/FreeRTOS.cpp index 40e5bbf3..1920fa43 100644 --- a/cpp_utils/FreeRTOS.cpp +++ b/cpp_utils/FreeRTOS.cpp @@ -67,6 +67,8 @@ uint32_t FreeRTOS::getTimeSinceStart() { */ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + m_owner = owner; if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); @@ -74,8 +76,6 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { xSemaphoreTake(m_semaphore, portMAX_DELAY); } - m_owner = owner; - if (m_usePthreads) { pthread_mutex_unlock(&m_pthread_mutex); } else { @@ -83,7 +83,6 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { } ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); - m_owner = std::string(""); return m_value; } // wait @@ -93,7 +92,8 @@ FreeRTOS::Semaphore::Semaphore(std::string name) { if (m_usePthreads) { pthread_mutex_init(&m_pthread_mutex, nullptr); } else { - m_semaphore = xSemaphoreCreateMutex(); + m_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(m_semaphore); } m_name = name; From fe3d318acddf87c6918944f24e8b899d63c816dd Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Thu, 1 Aug 2019 11:36:56 -0500 Subject: [PATCH 310/310] Update README.md --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index c785b3c6..fbea27ae 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,2 @@ # ESP32 Snippets -This repository hosts a set of ESP32 snippets that are in different -stages of completeness. They are made available in the hope that there -may be something of value to you and under the notion that something -is better than nothing. The samples are being categorized. - -* vfs - Virtual File System -* wifi - WiFi access -* curl - Examples of curl usage +This repository is no longer being actively maintained. It was previously archived to make it read-only but, by request, has been un-archived so that others may continue to post comments and issues. However, please understand that issues raised are unlikely to be resolved against this repository.