From 16801f3dacf5e913ffef0c53051b83a8d155413a Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 5 Mar 2020 15:30:20 +0100 Subject: [PATCH] - Rework ExpiringTimeTracker to be based on PolledTimeout. - Ensure espnowDelay and floodingMeshDelay always performs maintenance. - Rework MutexTracker to use shared_ptr. - Change enums to enum class. - Change typedef to using. - Add HeapMonitor class. - Make _messageIDs be a map instead of an unordered_map to reduce heap usage. - Use the possibly broken wifi_country ESP8266 API to check for legal WiFi channels when setting WiFi channels. - Make MessageData, RequestData and ResponseData contain a TimeTracker rather than inherit from TimeTracker. - Add deprecated attribute to TransmissionResult. - Remove superfluous elses. - Reduce cyclomatic complexity. - Change postfix ++ and -- to prefix. - Generalize getEncryptedConnectionIterator method. - Increase code NRVO compatibility. - Change _connectionAttemptTimeoutMs type from int32_t to uint32_t. - Add deprecated attribute to ESP8266WiFiMesh. - Add some constness to TypeConversionFunctions. - Move base36 arrays to PROGMEM in TypeConversionFunctions.cpp. - Add deprecated atttribute to SHA1 and MD5 hashes. - Remove _warningsEnabled in CryptoInterface since this has been replaced by the deprecated attribute. - Prefix all TypeConversion getters with "get". - Improve comments. - Fix merge conflict. --- .../examples/HelloEspnow/HelloEspnow.ino | 115 +++-- .../examples/HelloMesh/HelloMesh.ino | 4 +- .../examples/HelloTcpIp/HelloTcpIp.ino | 16 +- libraries/ESP8266WiFiMesh/keywords.txt | 2 +- .../ESP8266WiFiMesh/src/CryptoInterface.cpp | 94 ++-- .../ESP8266WiFiMesh/src/CryptoInterface.h | 64 +-- .../ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp | 12 +- .../ESP8266WiFiMesh/src/ESP8266WiFiMesh.h | 5 +- .../src/EncryptedConnectionData.cpp | 6 +- .../ESP8266WiFiMesh/src/EspnowMeshBackend.cpp | 421 +++++++++--------- .../ESP8266WiFiMesh/src/EspnowMeshBackend.h | 151 ++++--- .../src/EspnowProtocolInterpreter.h | 2 +- .../src/ExpiringTimeTracker.cpp | 97 +++- .../ESP8266WiFiMesh/src/ExpiringTimeTracker.h | 42 +- .../ESP8266WiFiMesh/src/FloodingMesh.cpp | 43 +- libraries/ESP8266WiFiMesh/src/FloodingMesh.h | 13 +- libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp | 63 +++ libraries/ESP8266WiFiMesh/src/HeapMonitor.h | 66 +++ .../ESP8266WiFiMesh/src/MeshBackendBase.cpp | 20 +- .../ESP8266WiFiMesh/src/MeshBackendBase.h | 32 +- libraries/ESP8266WiFiMesh/src/MessageData.cpp | 10 +- libraries/ESP8266WiFiMesh/src/MessageData.h | 4 +- .../ESP8266WiFiMesh/src/MutexTracker.cpp | 43 +- libraries/ESP8266WiFiMesh/src/MutexTracker.h | 20 +- .../ESP8266WiFiMesh/src/NetworkInfoBase.cpp | 2 +- libraries/ESP8266WiFiMesh/src/RequestData.cpp | 7 +- libraries/ESP8266WiFiMesh/src/RequestData.h | 8 +- .../ESP8266WiFiMesh/src/ResponseData.cpp | 33 +- libraries/ESP8266WiFiMesh/src/ResponseData.h | 8 +- .../ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp | 66 ++- .../ESP8266WiFiMesh/src/TcpIpMeshBackend.h | 28 +- libraries/ESP8266WiFiMesh/src/TimeTracker.h | 1 + .../src/TransmissionOutcome.cpp | 8 +- .../ESP8266WiFiMesh/src/TransmissionOutcome.h | 20 +- .../ESP8266WiFiMesh/src/TransmissionResult.h | 13 +- .../src/TypeConversionFunctions.cpp | 46 +- .../src/TypeConversionFunctions.h | 16 +- .../ESP8266WiFiMesh/src/UtilityFunctions.cpp | 2 +- 38 files changed, 924 insertions(+), 679 deletions(-) create mode 100644 libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp create mode 100644 libraries/ESP8266WiFiMesh/src/HeapMonitor.h diff --git a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino index 3a4225e58..3a03b8416 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino @@ -1,4 +1,4 @@ -#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. #include #include @@ -40,7 +40,7 @@ unsigned int responseNumber = 0; const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII String manageRequest(const String &request, MeshBackendBase &meshInstance); -transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance); +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance); void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance); @@ -55,27 +55,22 @@ EspnowMeshBackend espnowNode = EspnowMeshBackend(manageRequest, manageResponse, @return The string to send back to the other node. For ESP-NOW, return an empy string ("") if no response should be sent. */ String manageRequest(const String &request, MeshBackendBase &meshInstance) { - // We do not store strings in flash (via F()) in this function. - // The reason is that the other node will be waiting for our response, - // so keeping the strings in RAM will give a (small) improvement in response time. - // Of course, it is advised to adjust this approach based on RAM requirements. - // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { - String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission"; - Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): "); + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { (void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. - Serial.print("TCP/IP: "); + Serial.print(F("TCP/IP: ")); } else { - Serial.print("UNKNOWN!: "); + Serial.print(F("UNKNOWN!: ")); } /* Print out received message */ // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. // If you need to print the whole String it is better to store it and print it in the loop() later. // Note that request.substring will not work as expected if the String contains null values as data. - Serial.print("Request received: "); + Serial.print(F("Request received: ")); if (request.charAt(0) == 0) { Serial.println(request); // substring will not work for multiStrings. @@ -84,7 +79,7 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) { } /* return a string to send back */ - return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + " with AP MAC " + WiFi.softAPmacAddress() + "."); + return (String(F("Hello world response #")) + String(responseNumber++) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + F(" with AP MAC ") + WiFi.softAPmacAddress() + String('.')); } /** @@ -94,15 +89,15 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) { @param meshInstance The MeshBackendBase instance that called the function. @return The status code resulting from the response, as an int */ -transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance) { - transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) { + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { - String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission"; - Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): "); + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { - Serial.print("TCP/IP: "); + Serial.print(F("TCP/IP: ")); // Getting the sent message like this will work as long as ONLY(!) TCP/IP is used. // With TCP/IP the response will follow immediately after the request, so the stored message will not have changed. @@ -111,7 +106,7 @@ transmission_status_t manageResponse(const String &response, MeshBackendBase &me Serial.print(F("Request sent: ")); Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100)); } else { - Serial.print("UNKNOWN!: "); + Serial.print(F("UNKNOWN!: ")); } /* Print out received message */ @@ -146,7 +141,7 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) { } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { tcpIpInstance->connectionQueue().push_back(networkIndex); } else { - Serial.println(String(F("Invalid mesh backend!"))); + Serial.println(F("Invalid mesh backend!")); } } } @@ -177,7 +172,7 @@ bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) String targetMeshName = firstTransmission.substring(0, metadataEndIndex); - if (targetMeshName != "" && meshInstance.getMeshName() != targetMeshName) { + if (!targetMeshName.isEmpty() && meshInstance.getMeshName() != targetMeshName) { return false; // Broadcast is for another mesh network } else { // Remove metadata from message and mark as accepted broadcast. @@ -274,7 +269,7 @@ void setup() { // Storing our message in the EspnowMeshBackend instance is not required, but can be useful for organizing code, especially when using many EspnowMeshBackend instances. // Note that calling the multi-recipient versions of espnowNode.attemptTransmission and espnowNode.attemptAutoEncryptingTransmission will replace the stored message with whatever message is transmitted. // Also note that the maximum allowed number of ASCII characters in a ESP-NOW message is given by EspnowMeshBackend::getMaxMessageLength(). - espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F("."))); + espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.')); espnowNode.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook); espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook); @@ -290,7 +285,7 @@ void setup() { // Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received. // All nodes this node wishes to communicate with must then also use encrypted messages with the same getEspnowMessageEncryptionKey(), or messages will not be accepted. // Note that using AEAD encrypted messages will reduce the number of message bytes that can be transmitted. - //espnowNode.setEspnowMessageEncryptionKey("ChangeThisKeySeed_TODO"); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used. + //espnowNode.setEspnowMessageEncryptionKey(F("ChangeThisKeySeed_TODO")); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used. //espnowNode.setUseEncryptedMessages(true); } @@ -306,11 +301,11 @@ void loop() { EspnowMeshBackend::performEspnowMaintenance(); if (millis() - timeOfLastScan > 10000) { // Give other nodes some time to connect between data transfers. - Serial.println("\nPerforming unencrypted ESP-NOW transmissions."); + Serial.println(F("\nPerforming unencrypted ESP-NOW transmissions.")); uint32_t startTime = millis(); espnowNode.attemptTransmission(espnowNode.getMessage()); - Serial.println("Scan and " + String(espnowNode.latestTransmissionOutcomes().size()) + " transmissions done in " + String(millis() - startTime) + " ms."); + Serial.println(String(F("Scan and ")) + String(espnowNode.latestTransmissionOutcomes().size()) + F(" transmissions done in ") + String(millis() - startTime) + F(" ms.")); timeOfLastScan = millis(); @@ -328,19 +323,19 @@ void loop() { Serial.println(F("No mesh AP found.")); } else { for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) { - if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_FAILED) { + if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_FAILED) { Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID()); - } else if (transmissionOutcome.transmissionStatus() == TS_CONNECTION_FAILED) { + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::CONNECTION_FAILED) { Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID()); - } else if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_COMPLETE) { + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { // No need to do anything, transmission was successful. } else { - Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String(F("!"))); + Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!')); assert(F("Invalid transmission status returned from responseHandler!") && false); } } - Serial.println("\nPerforming ESP-NOW broadcast."); + Serial.println(F("\nPerforming ESP-NOW broadcast.")); startTime = millis(); @@ -348,9 +343,9 @@ void loop() { // Note that data that comes before broadcastMetadataDelimiter should not contain any broadcastMetadataDelimiter characters, // otherwise the broadcastFilter function used in this example file will not work. String broadcastMetadata = espnowNode.getMeshName() + String(broadcastMetadataDelimiter); - String broadcastMessage = String(F("Broadcast #")) + String(requestNumber) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F(".")); + String broadcastMessage = String(F("Broadcast #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'); espnowNode.broadcast(broadcastMetadata + broadcastMessage); - Serial.println("Broadcast to all mesh nodes done in " + String(millis() - startTime) + " ms."); + Serial.println(String(F("Broadcast to all mesh nodes done in ")) + String(millis() - startTime) + F(" ms.")); espnowDelay(100); // Wait for responses (broadcasts can receive an unlimited number of responses, other transmissions can only receive one response). @@ -358,25 +353,25 @@ void loop() { // You can use String::c_str() or String::begin() to retreive the data array later. // Note that certain String methods such as String::substring use null values to determine String length, which means they will not work as normal with multiStrings. uint8_t dataArray[] = {0, '\'', 0, '\'', ' ', '(', 'n', 'u', 'l', 'l', ')', ' ', 'v', 'a', 'l', 'u', 'e'}; - String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F(".")); - Serial.println("\nTransmitting: " + espnowMessage); + String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); espnowDelay(100); // Wait for response. - Serial.println("\nPerforming encrypted ESP-NOW transmissions."); + Serial.println(F("\nPerforming encrypted ESP-NOW transmissions.")); uint8_t targetBSSID[6] {0}; // We can create encrypted connections to individual nodes so that all ESP-NOW communication with the node will be encrypted. - if (espnowNode.constConnectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == ECS_CONNECTION_ESTABLISHED) { + if (espnowNode.constConnectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { // The WiFi scan will detect the AP MAC, but this will automatically be converted to the encrypted STA MAC by the framework. String peerMac = TypeCast::macToString(targetBSSID); - Serial.println("Encrypted ESP-NOW connection with " + peerMac + " established!"); + Serial.println(String(F("Encrypted ESP-NOW connection with ")) + peerMac + F(" established!")); // Making a transmission now will cause messages to targetBSSID to be encrypted. - String espnowMessage = "This message is encrypted only when received by node " + peerMac; - Serial.println("\nTransmitting: " + espnowMessage); + String espnowMessage = String(F("This message is encrypted only when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); espnowDelay(100); // Wait for response. @@ -389,48 +384,48 @@ void loop() { espnowNode.removeEncryptedConnection(targetBSSID); // Note that the peer will still be encrypted, so although we can send unencrypted messages to the peer, we cannot read the encrypted responses it sends back. - espnowMessage = "This message is no longer encrypted when received by node " + peerMac; - Serial.println("\nTransmitting: " + espnowMessage); + espnowMessage = String(F("This message is no longer encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); espnowDelay(100); // Wait for response. - Serial.println("Cannot read the encrypted response..."); + Serial.println(F("Cannot read the encrypted response...")); // Let's re-add our stored connection so we can communicate properly with targetBSSID again! espnowNode.addEncryptedConnection(serializedEncryptedConnection); - espnowMessage = "This message is once again encrypted when received by node " + peerMac; - Serial.println("\nTransmitting: " + espnowMessage); + espnowMessage = String(F("This message is once again encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); espnowDelay(100); // Wait for response. Serial.println(); // If we want to remove the encrypted connection on both nodes, we can do it like this. - encrypted_connection_removal_outcome_t removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID); - if (removalOutcome == ECRO_REMOVAL_SUCCEEDED) { - Serial.println(peerMac + " is no longer encrypted!"); + EncryptedConnectionRemovalOutcome removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID); + if (removalOutcome == EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED) { + Serial.println(peerMac + F(" is no longer encrypted!")); - espnowMessage = "This message is only received by node " + peerMac + ". Transmitting in this way will not change the transmission state of the sender."; - Serial.println("Transmitting: " + espnowMessage); + espnowMessage = String(F("This message is only received by node ")) + peerMac + F(". Transmitting in this way will not change the transmission state of the sender."); + Serial.println(String(F("Transmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, EspnowNetworkInfo(targetBSSID)); espnowDelay(100); // Wait for response. Serial.println(); // Of course, we can also just create a temporary encrypted connection that will remove itself once its duration has passed. - if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == ECS_CONNECTION_ESTABLISHED) { + if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { espnowDelay(42); uint32_t remainingDuration = 0; EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); - espnowMessage = "Messages this node sends to " + peerMac + " will be encrypted for " + String(remainingDuration) + " ms more."; - Serial.println("\nTransmitting: " + espnowMessage); + espnowMessage = String(F("Messages this node sends to ")) + peerMac + F(" will be encrypted for ") + String(remainingDuration) + F(" ms more."); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); espnowDelay(remainingDuration + 100); - espnowMessage = "Due to encrypted connection expiration, this message is no longer encrypted when received by node " + peerMac; - Serial.println("\nTransmitting: " + espnowMessage); + espnowMessage = String(F("Due to encrypted connection expiration, this message is no longer encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptTransmission(espnowMessage, false); espnowDelay(100); // Wait for response. } @@ -438,24 +433,24 @@ void loop() { // Or if we prefer we can just let the library automatically create brief encrypted connections which are long enough to transmit an encrypted message. // Note that encrypted responses will not be received, unless there already was an encrypted connection established with the peer before attemptAutoEncryptingTransmission was called. // This can be remedied via the requestPermanentConnections argument, though it must be noted that the maximum number of encrypted connections supported at a time is 6. - espnowMessage = "This message is always encrypted, regardless of receiver."; - Serial.println("\nTransmitting: " + espnowMessage); + espnowMessage = F("This message is always encrypted, regardless of receiver."); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptAutoEncryptingTransmission(espnowMessage); espnowDelay(100); // Wait for response. } else { - Serial.println("Ooops! Encrypted connection removal failed. Status: " + String(removalOutcome)); + Serial.println(String(F("Ooops! Encrypted connection removal failed. Status: ")) + String(static_cast(removalOutcome))); } // Finally, should you ever want to stop other parties from sending unencrypted messages to the node // setAcceptsUnencryptedRequests(false); // can be used for this. It applies to both encrypted connection requests and regular transmissions. - Serial.println("\n##############################################################################################"); + Serial.println(F("\n##############################################################################################")); } // Our last request was sent to all nodes found, so time to create a new request. - espnowNode.setMessage(String(F("Hello world request #")) + String(++requestNumber) + String(F(" from ")) - + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F("."))); + espnowNode.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ") + + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.')); } Serial.println(); diff --git a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino index 22836e17c..5855604b3 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino @@ -5,7 +5,7 @@ That way you will get instant confirmation of the mesh communication without checking the Serial Monitor. */ -#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. #include #include @@ -175,7 +175,7 @@ void loop() { floodingMeshDelay(1); // If you wish to transmit only to a single node, try using one of the following methods (requires the node to be within range and know the MAC of the recipient): - // Unencrypted: transmission_status_t floodingMesh.getEspnowMeshBackend().attemptTransmission(message, EspnowNetworkInfo(recipientMac)); + // Unencrypted: TransmissionStatusType floodingMesh.getEspnowMeshBackend().attemptTransmission(message, EspnowNetworkInfo(recipientMac)); // Encrypted (slow): floodingMesh.getEspnowMeshBackend().attemptAutoEncryptingTransmission(message, EspnowNetworkInfo(recipientMac)); if (theOne) { diff --git a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino index d91946e0f..3fad54aa1 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino @@ -1,4 +1,4 @@ -#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. #include #include @@ -25,7 +25,7 @@ unsigned int requestNumber = 0; unsigned int responseNumber = 0; String manageRequest(const String &request, MeshBackendBase &meshInstance); -transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance); +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance); void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); /* Create the mesh node object */ @@ -68,8 +68,8 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) { @param meshInstance The MeshBackendBase instance that called the function. @return The status code resulting from the response, as an int */ -transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance) { - transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) { + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { @@ -142,7 +142,7 @@ bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) { // The default hook only returns true and does nothing else. if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { - if (tcpIpInstance->latestTransmissionOutcomes().back().transmissionStatus() == TS_TRANSMISSION_COMPLETE) { + if (tcpIpInstance->latestTransmissionOutcomes().back().transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { // Our last request got a response, so time to create a new request. meshInstance.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + String('.')); @@ -209,11 +209,11 @@ void loop() { Serial.println(F("No mesh AP found.")); } else { for (TransmissionOutcome &transmissionOutcome : tcpIpNode.latestTransmissionOutcomes()) { - if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_FAILED) { + if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_FAILED) { Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID()); - } else if (transmissionOutcome.transmissionStatus() == TS_CONNECTION_FAILED) { + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::CONNECTION_FAILED) { Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID()); - } else if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_COMPLETE) { + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { // No need to do anything, transmission was successful. } else { Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!')); diff --git a/libraries/ESP8266WiFiMesh/keywords.txt b/libraries/ESP8266WiFiMesh/keywords.txt index 3574a231d..3e1088a6b 100644 --- a/libraries/ESP8266WiFiMesh/keywords.txt +++ b/libraries/ESP8266WiFiMesh/keywords.txt @@ -12,7 +12,7 @@ ESP8266WiFiMesh KEYWORD3 # Datatypes (KEYWORD1) ####################################### -transmission_status_t KEYWORD1 +TransmissionStatusType KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/libraries/ESP8266WiFiMesh/src/CryptoInterface.cpp b/libraries/ESP8266WiFiMesh/src/CryptoInterface.cpp index 449be1d33..8e0777bab 100644 --- a/libraries/ESP8266WiFiMesh/src/CryptoInterface.cpp +++ b/libraries/ESP8266WiFiMesh/src/CryptoInterface.cpp @@ -34,8 +34,6 @@ namespace { size_t _ctMinDataLength = 0; size_t _ctMaxDataLength = 1024; - - bool _warningsEnabled = true; br_hkdf_context _storedHkdfContext; bool _hkdfContextStored = false; @@ -186,52 +184,61 @@ namespace createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength); return TypeCast::uint8ArrayToHexString(hmac, hmacLength); } -} -namespace CryptoInterface -{ - void setCtMinDataLength(const size_t ctMinDataLength) + + // Helper function to avoid deprecated warnings. + void *md5HashHelper(const void *data, const size_t dataLength, void *resultArray) { - assert(ctMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF); - _ctMinDataLength = ctMinDataLength; - } - size_t ctMinDataLength() {return _ctMinDataLength;} - - void setCtMaxDataLength(const size_t ctMaxDataLength) - { - assert(ctMaxDataLength - ctMinDataLength() <= CT_MAX_DIFF); - _ctMaxDataLength = ctMaxDataLength; - } - size_t ctMaxDataLength() {return _ctMaxDataLength;} - - void setWarningsEnabled(bool warningsEnabled) { _warningsEnabled = warningsEnabled; } - bool warningsEnabled() { return _warningsEnabled; } - - void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; } - nonceGeneratorType getNonceGenerator() { return _nonceGenerator; } - - - // #################### MD5 #################### - - // resultArray must have size MD5_NATURAL_LENGTH or greater - void *md5Hash(const void *data, const size_t dataLength, void *resultArray) - { - if(warningsEnabled()) - Serial.println(F("\nWARNING! The MD5 hash is broken in terms of attacker resistance.\n" - "Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n" - "Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n")); - br_md5_context context; br_md5_init(&context); br_md5_update(&context, data, dataLength); br_md5_out(&context, resultArray); return resultArray; } + + // Helper function to avoid deprecated warnings. + void *sha1HashHelper(const void *data, const size_t dataLength, void *resultArray) + { + br_sha1_context context; + br_sha1_init(&context); + br_sha1_update(&context, data, dataLength); + br_sha1_out(&context, resultArray); + return resultArray; + } +} + +namespace CryptoInterface +{ + void setCtMinDataLength(const size_t ctMinDataLength) + { + assert(getCtMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF); + _ctMinDataLength = ctMinDataLength; + } + size_t getCtMinDataLength() {return _ctMinDataLength;} + + void setCtMaxDataLength(const size_t ctMaxDataLength) + { + assert(ctMaxDataLength - getCtMinDataLength() <= CT_MAX_DIFF); + _ctMaxDataLength = ctMaxDataLength; + } + size_t getCtMaxDataLength() {return _ctMaxDataLength;} + + void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; } + nonceGeneratorType getNonceGenerator() { return _nonceGenerator; } + + + // #################### MD5 #################### + + // resultArray must have size MD5_NATURAL_LENGTH or greater + void *md5Hash(const void *data, const size_t dataLength, void *resultArray) + { + return md5HashHelper(data, dataLength, resultArray); + } String md5Hash(const String &message) { uint8_t hash[MD5_NATURAL_LENGTH]; - md5Hash(message.c_str(), message.length(), hash); + md5HashHelper(message.c_str(), message.length(), hash); return TypeCast::uint8ArrayToHexString(hash, MD5_NATURAL_LENGTH); } @@ -260,23 +267,14 @@ namespace CryptoInterface // resultArray must have size SHA1_NATURAL_LENGTH or greater void *sha1Hash(const void *data, const size_t dataLength, void *resultArray) - { - if(warningsEnabled()) - Serial.println(F("\nWARNING! The SHA-1 hash is broken in terms of attacker resistance.\n" - "Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n" - "Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n")); - - br_sha1_context context; - br_sha1_init(&context); - br_sha1_update(&context, data, dataLength); - br_sha1_out(&context, resultArray); - return resultArray; + { + return sha1HashHelper(data, dataLength, resultArray); } String sha1Hash(const String &message) { uint8_t hash[SHA1_NATURAL_LENGTH]; - sha1Hash(message.c_str(), message.length(), hash); + sha1HashHelper(message.c_str(), message.length(), hash); return TypeCast::uint8ArrayToHexString(hash, SHA1_NATURAL_LENGTH); } diff --git a/libraries/ESP8266WiFiMesh/src/CryptoInterface.h b/libraries/ESP8266WiFiMesh/src/CryptoInterface.h index b29d93abd..9efc0df0c 100644 --- a/libraries/ESP8266WiFiMesh/src/CryptoInterface.h +++ b/libraries/ESP8266WiFiMesh/src/CryptoInterface.h @@ -41,8 +41,8 @@ namespace CryptoInterface * naturally yielding constant-time operations. HMAC is naturally as constant-time as the underlying hash function. The size of the MACed data, and the size of the key, * may leak, though; only the contents are protected." * - * For messages much smaller than ctMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC, - * determined by the size of (ctMaxDataLength() - ctMinDataLength()). + * For messages much smaller than getCtMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC, + * determined by the size of (getCtMaxDataLength() - getCtMinDataLength()). * Constant-time processing also sets limits on the data length. * * Making the fixed data length limits variable will generally defeat the purpose of using constant-time. @@ -78,34 +78,26 @@ namespace CryptoInterface * It should not be changed once a constant time function has been used at least once. * Otherwise the constant time will not be constant for the used functions. * - * The difference ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte). + * The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte). */ void setCtMinDataLength(const size_t ctMinDataLength); /** * 0 by default. */ - size_t ctMinDataLength(); + size_t getCtMinDataLength(); /** * This function allows for fine-tuning of the specifications for the constant time calculations. * It should not be changed once a constant time function has been used at least once. * Otherwise the constant time will not be constant for the used functions. * - * The difference ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte). + * The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte). */ void setCtMaxDataLength(const size_t ctMaxDataLength); /** * 1024 by default. */ - size_t ctMaxDataLength(); - - /** - * Turn on or off warning Serial prints from the CryptoInterface functions. - * - * @param warningsEnabled If true, warnings will be printed to Serial. - */ - void setWarningsEnabled(bool warningsEnabled); - bool warningsEnabled(); + size_t getCtMaxDataLength(); /** * Set the nonce generator used by the CryptoInterface functions. @@ -119,6 +111,9 @@ namespace CryptoInterface // #################### MD5 #################### /** + * WARNING! The MD5 hash is broken in terms of attacker resistance. + * Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise. + * * Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and stored in resultArray. * Uses the BearSSL cryptographic library. * @@ -128,9 +123,12 @@ namespace CryptoInterface * * @return A pointer to resultArray. */ - void *md5Hash(const void *data, const size_t dataLength, void *resultArray); + void *md5Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated)); /** + * WARNING! The MD5 hash is broken in terms of attacker resistance. + * Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise. + * * Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and returned as a String in HEX format. * Uses the BearSSL cryptographic library. * @@ -138,7 +136,7 @@ namespace CryptoInterface * * @return A String with the generated hash in HEX format. */ - String md5Hash(const String &message); + String md5Hash(const String &message) __attribute__((deprecated)); /** * Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray. @@ -176,7 +174,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -193,7 +191,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to MD5_NATURAL_LENGTH. @@ -206,6 +204,9 @@ namespace CryptoInterface // #################### SHA-1 #################### /** + * WARNING! The SHA-1 hash is broken in terms of attacker resistance. + * Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise. + * * Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and stored in resultArray. * Uses the BearSSL cryptographic library. * @@ -215,9 +216,12 @@ namespace CryptoInterface * * @return A pointer to resultArray. */ - void *sha1Hash(const void *data, const size_t dataLength, void *resultArray); + void *sha1Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated)); /** + * WARNING! The SHA-1 hash is broken in terms of attacker resistance. + * Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise. + * * Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and returned as a String in HEX format. * Uses the BearSSL cryptographic library. * @@ -225,7 +229,7 @@ namespace CryptoInterface * * @return A String with the generated hash in HEX format. */ - String sha1Hash(const String &message); + String sha1Hash(const String &message) __attribute__((deprecated)); /** * Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray. @@ -263,7 +267,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -280,7 +284,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA1_NATURAL_LENGTH. @@ -350,7 +354,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -367,7 +371,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA224_NATURAL_LENGTH. @@ -437,7 +441,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -454,7 +458,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA256_NATURAL_LENGTH. @@ -524,7 +528,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -541,7 +545,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA384_NATURAL_LENGTH. @@ -611,7 +615,7 @@ namespace CryptoInterface * Uses the BearSSL cryptographic library. * * @param data The data array from which to create the HMAC. - * @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param resultArray The array wherein to store the resulting HMAC. @@ -628,7 +632,7 @@ namespace CryptoInterface * Constant-time version. * Uses the BearSSL cryptographic library. * - * @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()]. + * @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()]. * @param hashKey The hash key to use when creating the HMAC. * @param hashKeyLength The length of the hash key in bytes. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA512_NATURAL_LENGTH. diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp index 19e64d69f..1678f9d00 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp @@ -54,7 +54,7 @@ namespace TypeCast = MeshTypeConversionFunctions; const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress(); -String ESP8266WiFiMesh::lastSSID = ""; +String ESP8266WiFiMesh::lastSSID; bool ESP8266WiFiMesh::staticIPActivated = false; // IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server. @@ -88,9 +88,9 @@ ESP8266WiFiMesh::ESP8266WiFiMesh(ESP8266WiFiMesh::requestHandlerType requestHand void ESP8266WiFiMesh::updateNetworkNames(const String &newMeshName, const String &newNodeID) { - if(newMeshName != "") + if(!newMeshName.isEmpty()) _meshName = newMeshName; - if(newNodeID != "") + if(!newNodeID.isEmpty()) _nodeID = newNodeID; String newSSID = _meshName + _nodeID; @@ -469,7 +469,7 @@ void ESP8266WiFiMesh::initiateConnectionToAP(const String &targetSSID, int targe */ transmission_status_t ESP8266WiFiMesh::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) { - if(staticIPActivated && lastSSID != "" && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. + if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. { #if LWIP_VERSION_MAJOR >= 2 // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. @@ -567,12 +567,12 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding WiFi.disconnect(); yield(); - String currentSSID = ""; + String currentSSID; int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT; uint8_t *currentBSSID = NULL; // If an SSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change. - if(currentNetwork.SSID != "") + if(!currentNetwork.SSID.isEmpty()) { currentSSID = currentNetwork.SSID; currentWiFiChannel = currentNetwork.wifiChannel; diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h index 139b2e980..8647502bb 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h @@ -47,8 +47,9 @@ #include #include #include -#include "NetworkInfo.h" #include "TransmissionResult.h" +#include "NetworkInfo.h" + const String WIFI_MESH_EMPTY_STRING = ""; @@ -175,7 +176,7 @@ public: */ ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, - uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011); + uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011) __attribute__((deprecated)); /** * A vector that contains the NetworkInfo for each WiFi network to connect to. diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp index 486bf7844..9f5cc45d3 100644 --- a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp @@ -102,10 +102,8 @@ bool EncryptedConnectionData::connectedTo(const uint8_t *peerMac) const { return true; } - else - { - return false; - } + + return false; } void EncryptedConnectionData::setHashKey(const uint8_t hashKey[espnowHashKeyLength]) diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp index ee10b3416..ceb458cd8 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp @@ -39,9 +39,9 @@ static const uint64_t uint64MSB = 0x8000000000000000; const uint8_t EspnowMeshBackend::broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -bool EspnowMeshBackend::_espnowTransmissionMutex = false; -bool EspnowMeshBackend::_espnowConnectionQueueMutex = false; -bool EspnowMeshBackend::_responsesToSendMutex = false; +std::shared_ptr EspnowMeshBackend::_espnowTransmissionMutex = std::make_shared(false); +std::shared_ptr EspnowMeshBackend::_espnowConnectionQueueMutex = std::make_shared(false); +std::shared_ptr EspnowMeshBackend::_responsesToSendMutex = std::make_shared(false); EspnowMeshBackend *EspnowMeshBackend::_espnowRequestManager = nullptr; @@ -67,8 +67,8 @@ bool EspnowMeshBackend::_espnowSendConfirmed = false; String EspnowMeshBackend::_ongoingPeerRequestNonce; uint8_t EspnowMeshBackend::_ongoingPeerRequestMac[6] = {0}; EspnowMeshBackend *EspnowMeshBackend::_ongoingPeerRequester = nullptr; -encrypted_connection_status_t EspnowMeshBackend::_ongoingPeerRequestResult = ECS_MAX_CONNECTIONS_REACHED_SELF; -uint32_t EspnowMeshBackend::_ongoingPeerRequestEncryptionStart = 0; +EncryptedConnectionStatus EspnowMeshBackend::_ongoingPeerRequestResult = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; +ExpiringTimeTracker _ongoingPeerRequestEncryptionTimeout([](){ return EspnowMeshBackend::getEncryptionRequestTimeout(); }); bool EspnowMeshBackend::_reciprocalPeerRequestConfirmation = false; uint8_t EspnowMeshBackend::_espnowEncryptionKok[espnowEncryptedConnectionKeyLength] = { 0 }; @@ -84,13 +84,13 @@ uint32_t EspnowMeshBackend::_unsynchronizedMessageID = 0; // so storage duration should not be too long. uint32_t EspnowMeshBackend::_logEntryLifetimeMs = 2500; uint32_t EspnowMeshBackend::_broadcastResponseTimeoutMs = 1000; // This is shorter than _logEntryLifetimeMs to preserve RAM since broadcasts are not deleted from sentRequests until they expire. -uint32_t EspnowMeshBackend::_timeOfLastLogClear = 0; +ExpiringTimeTracker _logClearingCooldown(500); uint32_t EspnowMeshBackend::_criticalHeapLevel = 6000; // In bytes uint32_t EspnowMeshBackend::_criticalHeapLevelBuffer = 6000; // In bytes uint8_t EspnowMeshBackend::_maxTransmissionsPerMessage = 3; -bool EspnowMeshBackend::_espnowSendToNodeMutex = false; +std::shared_ptr EspnowMeshBackend::_espnowSendToNodeMutex = std::make_shared(false); uint8_t EspnowMeshBackend::_transmissionTargetBSSID[6] = {0}; double EspnowMeshBackend::_transmissionsTotal = 0; @@ -100,19 +100,22 @@ bool EspnowMeshBackend::_staticVerboseMode = false; void espnowDelay(uint32_t durationMs) { - uint32_t startingTime = millis(); - - while(millis() - startingTime < durationMs) + ExpiringTimeTracker timeout(durationMs); + + do { + // We want to delay before performEspnowMaintenance() so background tasks can be managed first. + // Initial while combined with YieldAndDelayMs polledTimeout::YieldPolicy is not suitable since the delay then occurs before evaluating the condition (meaning durationMs = 1 never executes the loop interior). delay(1); EspnowMeshBackend::performEspnowMaintenance(); } + while(!timeout); } EspnowMeshBackend::EspnowMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, broadcastFilterType broadcastFilter, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel) - : MeshBackendBase(requestHandler, responseHandler, networkFilter, MB_ESP_NOW) + : MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::ESP_NOW) { // Reserve the maximum possible usage early on to prevent heap fragmentation later. encryptedConnections.reserve(maxEncryptedConnections); @@ -259,7 +262,7 @@ void EspnowMeshBackend::performEspnowMaintenance(uint32_t estimatedMaxDuration) return; } - if(millis() - _timeOfLastLogClear >= 500) // Clearing too frequently will cause a lot of unnecessary container iterations. + if(_logClearingCooldown) // Clearing too frequently will cause a lot of unnecessary container iterations. { clearOldLogEntries(); } @@ -313,6 +316,21 @@ void EspnowMeshBackend::deleteExpiredLogEntries(std::map, { for(typename std::map, T>::iterator entryIterator = logEntries.begin(); entryIterator != logEntries.end(); ) + { + if(entryIterator->second.getTimeTracker().timeSinceCreation() > maxEntryLifetimeMs) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +template +void EspnowMeshBackend::deleteExpiredLogEntries(std::map, TimeTracker> &logEntries, uint32_t maxEntryLifetimeMs) +{ + for(typename std::map, TimeTracker>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) { if(entryIterator->second.timeSinceCreation() > maxEntryLifetimeMs) { @@ -329,7 +347,7 @@ void EspnowMeshBackend::deleteExpiredLogEntries(std::mapfirst.first == uint64BroadcastMac; - uint32_t timeSinceCreation = entryIterator->second.timeSinceCreation(); + uint32_t timeSinceCreation = entryIterator->second.getTimeTracker().timeSinceCreation(); if((!broadcast && timeSinceCreation > requestLifetimeMs) || (broadcast && timeSinceCreation > broadcastLifetimeMs)) @@ -347,7 +365,7 @@ void EspnowMeshBackend::deleteExpiredLogEntries(std::list &logEntries, uint32 for(typename std::list::iterator entryIterator = logEntries.begin(); entryIterator != logEntries.end(); ) { - if(entryIterator->timeSinceCreation() > maxEntryLifetimeMs) + if(entryIterator->getTimeTracker().timeSinceCreation() > maxEntryLifetimeMs) { entryIterator = logEntries.erase(entryIterator); } @@ -363,7 +381,7 @@ void EspnowMeshBackend::deleteExpiredLogEntries(std::listtemporary(); - if(timeTrackerPointer && timeTrackerPointer->timeSinceCreation() > maxEntryLifetimeMs) + if(timeTrackerPointer && timeTrackerPointer->elapsedTime() > maxEntryLifetimeMs) { entryIterator = logEntries.erase(entryIterator); } @@ -379,7 +397,7 @@ void EspnowMeshBackend::deleteExpiredLogEntries(std::list &logEn entryIterator != logEntries.end(); ) { auto timeTrackerPointer = entryIterator->temporary(); - if(timeTrackerPointer && timeTrackerPointer->timeSinceCreation() > maxEntryLifetimeMs) + if(timeTrackerPointer && timeTrackerPointer->elapsedTime() > maxEntryLifetimeMs) { entryIterator = logEntries.erase(entryIterator); } @@ -394,7 +412,7 @@ void EspnowMeshBackend::clearOldLogEntries() // uint32_t startTime = millis(); - _timeOfLastLogClear = millis(); + _logClearingCooldown.reset(); deleteExpiredLogEntries(receivedEspnowTransmissions, logEntryLifetimeMs()); deleteExpiredLogEntries(receivedRequests, logEntryLifetimeMs()); // Just needs to be long enough to not accept repeated transmissions by mistake. @@ -668,7 +686,7 @@ void EspnowMeshBackend::handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t if(!encryptedConnectionEstablished(_ongoingPeerRequestResult) && JsonTranslator::verifyEncryptionRequestHmac(message, macaddr, apMacArray, _ongoingPeerRequester->getEspnowHashKey(), espnowHashKeyLength)) { - _ongoingPeerRequestEncryptionStart = millis(); + _ongoingPeerRequestEncryptionTimeout.reset(); connectionLogIterator existingEncryptedConnection = connectionLogEndIterator(); @@ -680,7 +698,7 @@ void EspnowMeshBackend::handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t else { // Encrypted connection already exists - _ongoingPeerRequestResult = ECS_CONNECTION_ESTABLISHED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::CONNECTION_ESTABLISHED; if(auto timeTrackerPointer = existingEncryptedConnection->temporary()) { @@ -715,15 +733,15 @@ void EspnowMeshBackend::handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t encryptedConnection->setOwnSessionKey(ownSessionKey); if(messageHeader == FPSTR(encryptedConnectionInfoHeader)) - _ongoingPeerRequestResult = ECS_CONNECTION_ESTABLISHED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::CONNECTION_ESTABLISHED; else if(messageHeader == FPSTR(softLimitEncryptedConnectionInfoHeader)) - _ongoingPeerRequestResult = ECS_SOFT_LIMIT_CONNECTION_ESTABLISHED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED; else assert(false && String(F("Unknown _ongoingPeerRequestResult!"))); } else { - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } _ongoingPeerRequestNonce.clear(); @@ -733,7 +751,7 @@ void EspnowMeshBackend::handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t { if(JsonTranslator::verifyEncryptionRequestHmac(message, macaddr, apMacArray, _ongoingPeerRequester->getEspnowHashKey(), espnowHashKeyLength)) { - _ongoingPeerRequestResult = ECS_MAX_CONNECTIONS_REACHED_PEER; + _ongoingPeerRequestResult = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_PEER; _ongoingPeerRequestNonce.clear(); } } @@ -815,27 +833,25 @@ void EspnowMeshBackend::espnowReceiveCallback(uint8_t *macaddr, uint8_t *dataArr else { std::map, MessageData>::iterator storedMessageIterator = receivedEspnowTransmissions.find(std::make_pair(macAndType, messageID)); - - if(storedMessageIterator != receivedEspnowTransmissions.end()) // If we have not stored the key already, we missed the first message part. - { - if(!storedMessageIterator->second.addToMessage(dataArray, len)) - { - // If we received the wrong message part, remove the whole message if we have missed a part. - // Otherwise just ignore the received part since it has already been stored. - - uint8_t transmissionsRemainingExpected = storedMessageIterator->second.getTransmissionsRemaining() - 1; - - if(transmissionsRemaining < transmissionsRemainingExpected) - { - receivedEspnowTransmissions.erase(storedMessageIterator); - return; - } - } - } - else + + if(storedMessageIterator == receivedEspnowTransmissions.end()) // If we have not stored the key already, we missed the first message part. { return; } + + if(!storedMessageIterator->second.addToMessage(dataArray, len)) + { + // If we received the wrong message part, remove the whole message if we have missed a part. + // Otherwise just ignore the received part since it has already been stored. + + uint8_t transmissionsRemainingExpected = storedMessageIterator->second.getTransmissionsRemaining() - 1; + + if(transmissionsRemaining < transmissionsRemainingExpected) + { + receivedEspnowTransmissions.erase(storedMessageIterator); + return; + } + } } //Serial.println("methodStart storage done " + String(millis() - methodStart)); @@ -913,9 +929,9 @@ bool EspnowMeshBackend::isEspnowRequestManager() return (this == getEspnowRequestManager()); } -bool EspnowMeshBackend::encryptedConnectionEstablished(encrypted_connection_status_t connectionStatus) +bool EspnowMeshBackend::encryptedConnectionEstablished(EncryptedConnectionStatus connectionStatus) { - return connectionStatus > 0; + return static_cast(connectionStatus) > 0; } void EspnowMeshBackend::setLogEntryLifetimeMs(uint32_t logEntryLifetimeMs) @@ -1057,7 +1073,7 @@ bool EspnowMeshBackend::usesConstantSessionKey(char messageType) return messageType == 'A' || messageType == 'C'; } -transmission_status_t EspnowMeshBackend::espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance) +TransmissionStatusType EspnowMeshBackend::espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance) { using EspnowProtocolInterpreter::synchronizationRequestHeader; @@ -1076,19 +1092,17 @@ transmission_status_t EspnowMeshBackend::espnowSendToNode(const String &message, if(encryptedConnection->desync()) { - return TS_TRANSMISSION_FAILED; + return TransmissionStatusType::TRANSMISSION_FAILED; } } return espnowSendToNodeUnsynchronized(message, encryptedMac, messageType, generateMessageID(encryptedConnection), espnowInstance); } - else - { - return espnowSendToNodeUnsynchronized(message, targetBSSID, messageType, generateMessageID(encryptedConnection), espnowInstance); - } + + return espnowSendToNodeUnsynchronized(message, targetBSSID, messageType, generateMessageID(encryptedConnection), espnowInstance); } -transmission_status_t EspnowMeshBackend::espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance) +TransmissionStatusType EspnowMeshBackend::espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance) { using namespace EspnowProtocolInterpreter; @@ -1096,7 +1110,7 @@ transmission_status_t EspnowMeshBackend::espnowSendToNodeUnsynchronized(const St if(!mutexTracker.mutexCaptured()) { assert(false && String(F("ERROR! espnowSendToNode already in progress. Don't call espnowSendToNode from callbacks as this will make it impossible to know which transmissions succeed! Aborting."))); - return TS_TRANSMISSION_FAILED; + return TransmissionStatusType::TRANSMISSION_FAILED; } // We copy the message String and bssid array from the arguments in this method to make sure they are @@ -1197,19 +1211,17 @@ transmission_status_t EspnowMeshBackend::espnowSendToNodeUnsynchronized(const St if(messageType == 'B') retransmissions = espnowInstance->getBroadcastTransmissionRedundancy(); - for(uint32_t i = 0; i <= retransmissions; i++) + for(uint32_t i = 0; i <= retransmissions; ++i) { _espnowSendConfirmed = false; - uint32_t transmissionStartTime = millis(); + ExpiringTimeTracker transmissionTimeout([](){ return getEspnowTransmissionTimeout(); }); - while(!_espnowSendConfirmed && millis() - transmissionStartTime < getEspnowTransmissionTimeout()) + while(!_espnowSendConfirmed && !transmissionTimeout) { if(esp_now_send(_transmissionTargetBSSID, transmission, transmissionSize) == 0) // == 0 => Success { - uint32_t transmissionAttemptStart = millis(); - while(!_espnowSendConfirmed - && (millis() - transmissionAttemptStart < getEspnowRetransmissionInterval()) - && (millis() - transmissionStartTime < getEspnowTransmissionTimeout())) + ExpiringTimeTracker retransmissionTime([](){ return getEspnowRetransmissionInterval(); }); + while(!_espnowSendConfirmed && !retransmissionTime && !transmissionTimeout) { delay(1); // Note that callbacks can be called during delay time, so it is possible to receive a transmission during this delay. } @@ -1235,7 +1247,7 @@ transmission_status_t EspnowMeshBackend::espnowSendToNodeUnsynchronized(const St if(!_espnowSendConfirmed) { - _transmissionsFailed++; + ++_transmissionsFailed; staticVerboseModePrint(String(F("espnowSendToNode failed!"))); staticVerboseModePrint(String(F("Transmission #: ")) + String(transmissionsRequired - transmissionsRemaining) + String('/') + String(transmissionsRequired)); @@ -1244,27 +1256,27 @@ transmission_status_t EspnowMeshBackend::espnowSendToNodeUnsynchronized(const St if(messageStart && encryptedConnection && !usesConstantSessionKey(messageType) && encryptedConnection->getOwnSessionKey() == messageID) encryptedConnection->setDesync(true); - return TS_TRANSMISSION_FAILED; + return TransmissionStatusType::TRANSMISSION_FAILED; } - transmissionsRemaining--; // This is used when transfering multi-transmission messages. + --transmissionsRemaining; // This is used when transfering multi-transmission messages. } while(transmissionsRemaining >= 0); // Useful when debugging the protocol //staticVerboseModePrint("Sent to Mac: " + TypeCast::macToString(_transmissionTargetBSSID) + " ID: " + TypeCast::uint64ToString(messageID)); - return TS_TRANSMISSION_COMPLETE; + return TransmissionStatusType::TRANSMISSION_COMPLETE; } -transmission_status_t EspnowMeshBackend::sendRequest(const String &message, const uint8_t *targetBSSID) +TransmissionStatusType EspnowMeshBackend::sendRequest(const String &message, const uint8_t *targetBSSID) { - transmission_status_t transmissionStatus = espnowSendToNode(message, targetBSSID, 'Q', this); + TransmissionStatusType transmissionStatus = espnowSendToNode(message, targetBSSID, 'Q', this); return transmissionStatus; } -transmission_status_t EspnowMeshBackend::sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID) +TransmissionStatusType EspnowMeshBackend::sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID) { EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(targetBSSID); uint8_t encryptedMac[6] {0}; @@ -1278,7 +1290,7 @@ transmission_status_t EspnowMeshBackend::sendResponse(const String &message, uin return espnowSendToNodeUnsynchronized(message, encryptedConnection ? encryptedMac : targetBSSID, 'A', requestID, this); } -bool EspnowMeshBackend::transmissionInProgress(){return _espnowTransmissionMutex;} +bool EspnowMeshBackend::transmissionInProgress(){return *_espnowTransmissionMutex;} EspnowMeshBackend::macAndType_td EspnowMeshBackend::createMacAndTypeValue(uint64_t uint64Mac, char messageType) { @@ -1294,7 +1306,7 @@ void EspnowMeshBackend::setEspnowEncryptedConnectionKey(const uint8_t espnowEncr { assert(espnowEncryptedConnectionKey != nullptr); - for(int i = 0; i < espnowEncryptedConnectionKeyLength; i++) + for(int i = 0; i < espnowEncryptedConnectionKeyLength; ++i) { _espnowEncryptedConnectionKey[i] = espnowEncryptedConnectionKey[i]; } @@ -1321,7 +1333,7 @@ bool EspnowMeshBackend::setEspnowEncryptionKok(uint8_t espnowEncryptionKok[espno if(espnowEncryptionKok == nullptr || esp_now_set_kok(espnowEncryptionKok, espnowEncryptedConnectionKeyLength)) // esp_now_set_kok failed if not == 0 return false; - for(int i = 0; i < espnowEncryptedConnectionKeyLength; i++) + for(int i = 0; i < espnowEncryptedConnectionKeyLength; ++i) { _espnowEncryptionKok[i] = espnowEncryptionKok[i]; } @@ -1351,7 +1363,7 @@ void EspnowMeshBackend::setEspnowHashKey(const uint8_t espnowHashKey[espnowHashK { assert(espnowHashKey != nullptr); - for(int i = 0; i < espnowHashKeyLength; i++) + for(int i = 0; i < espnowHashKeyLength; ++i) { _espnowHashKey[i] = espnowHashKey[i]; } @@ -1371,7 +1383,7 @@ void EspnowMeshBackend::setEspnowMessageEncryptionKey(uint8_t espnowMessageEncry { assert(espnowMessageEncryptionKey != nullptr); - for(int i = 0; i < CryptoInterface::ENCRYPTION_KEY_LENGTH; i++) + for(int i = 0; i < CryptoInterface::ENCRYPTION_KEY_LENGTH; ++i) { _espnowMessageEncryptionKey[i] = espnowMessageEncryptionKey[i]; } @@ -1461,7 +1473,7 @@ std::list::const_iterator EspnowMeshBackend::getScheduledResponse( while(stepsToTarget > 0) { startFromBeginning ? ++responseIterator : --responseIterator; - stepsToTarget--; + --stepsToTarget; } return responseIterator; @@ -1542,7 +1554,7 @@ bool EspnowMeshBackend::addUnencryptedConnection(const String &serializedConnect return JsonTranslator::getUnsynchronizedMessageID(serializedConnectionState, _unsynchronizedMessageID); } -encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey) +EncryptedConnectionStatus EspnowMeshBackend::addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey) { assert(encryptedConnections.size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library @@ -1557,28 +1569,28 @@ encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(uint8_t esp_now_set_peer_key(peerStaMac, getEspnowEncryptedConnectionKey(encryptionKeyArray), espnowEncryptedConnectionKeyLength); encryptedConnection->setHashKey(getEspnowHashKey()); - return ECS_CONNECTION_ESTABLISHED; + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; } if(encryptedConnections.size() == maxEncryptedConnections) { // No capacity for more encrypted connections. - return ECS_MAX_CONNECTIONS_REACHED_SELF; + return EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; } // returns 0 on success: int esp_now_add_peer(u8 *mac_addr, u8 role, u8 channel, u8 *key, u8 key_len) // Only MAC, encryption key and key length (16) actually matter. The rest is not used by ESP-NOW. else if(0 == esp_now_add_peer(peerStaMac, ESP_NOW_ROLE_CONTROLLER, getWiFiChannel(), getEspnowEncryptedConnectionKey(encryptionKeyArray), espnowEncryptedConnectionKeyLength)) { encryptedConnections.emplace_back(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, getEspnowHashKey()); - return ECS_CONNECTION_ESTABLISHED; + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; } else { - return ECS_API_CALL_FAILED; + return EncryptedConnectionStatus::API_CALL_FAILED; } } -encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration) +EncryptedConnectionStatus EspnowMeshBackend::addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration) { uint32_t duration = 0; bool desync = false; @@ -1591,7 +1603,7 @@ encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(const St && JsonTranslator::getOwnSessionKey(serializedConnectionState, ownSessionKey) && JsonTranslator::getPeerSessionKey(serializedConnectionState, peerSessionKey) && JsonTranslator::getPeerStaMac(serializedConnectionState, peerStaMac) && JsonTranslator::getPeerApMac(serializedConnectionState, peerApMac)) { - encrypted_connection_status_t result = ECS_API_CALL_FAILED; + EncryptedConnectionStatus result = EncryptedConnectionStatus::API_CALL_FAILED; if(!ignoreDuration && JsonTranslator::getDuration(serializedConnectionState, duration)) { @@ -1602,7 +1614,7 @@ encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(const St result = addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); } - if(result == ECS_CONNECTION_ESTABLISHED) + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerStaMac); encryptedConnection->setDesync(desync); @@ -1610,13 +1622,11 @@ encrypted_connection_status_t EspnowMeshBackend::addEncryptedConnection(const St return result; } - else - { - return ECS_REQUEST_TRANSMISSION_FAILED; - } + + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } -encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration) +EncryptedConnectionStatus EspnowMeshBackend::addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration) { assert(encryptedConnections.size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library @@ -1637,12 +1647,12 @@ encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection encryptedConnection->setRemainingDuration(duration); } - return ECS_CONNECTION_ESTABLISHED; + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; } - encrypted_connection_status_t result = addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); + EncryptedConnectionStatus result = addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); - if(result == ECS_CONNECTION_ESTABLISHED) + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { if(!getEncryptedConnectionIterator(peerStaMac, encryptedConnection)) assert(false && String(F("No connection found despite being added in addTemporaryEncryptedConnection."))); @@ -1653,7 +1663,7 @@ encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection return result; } -encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration) +EncryptedConnectionStatus EspnowMeshBackend::addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration) { bool desync = false; uint64_t ownSessionKey = 0; @@ -1665,9 +1675,9 @@ encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection && JsonTranslator::getOwnSessionKey(serializedConnectionState, ownSessionKey) && JsonTranslator::getPeerSessionKey(serializedConnectionState, peerSessionKey) && JsonTranslator::getPeerStaMac(serializedConnectionState, peerStaMac) && JsonTranslator::getPeerApMac(serializedConnectionState, peerApMac)) { - encrypted_connection_status_t result = addTemporaryEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration); + EncryptedConnectionStatus result = addTemporaryEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration); - if(result == ECS_CONNECTION_ESTABLISHED) + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerStaMac); encryptedConnection->setDesync(desync); @@ -1675,10 +1685,8 @@ encrypted_connection_status_t EspnowMeshBackend::addTemporaryEncryptedConnection return result; } - else - { - return ECS_REQUEST_TRANSMISSION_FAILED; - } + + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } void EspnowMeshBackend::handlePostponedRemovals() @@ -1696,7 +1704,7 @@ void EspnowMeshBackend::handlePostponedRemovals() } } -encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder) +EncryptedConnectionStatus EspnowMeshBackend::requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder) { using namespace EspnowProtocolInterpreter; @@ -1706,7 +1714,7 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne if(!mutexTracker.mutexCaptured()) { assert(false && String(F("ERROR! Transmission in progress. Don't call requestEncryptedConnection from callbacks as this may corrupt program state! Aborting."))); - return ECS_REQUEST_TRANSMISSION_FAILED; + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } EncryptedConnectionLog *existingEncryptedConnection = getEncryptedConnection(peerMac); @@ -1718,12 +1726,12 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne assert(encryptedConnections.size() == maxEncryptedConnections); // No capacity for more encrypted connections. - return ECS_MAX_CONNECTIONS_REACHED_SELF; + return EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; } String requestNonce = TypeCast::macToString(peerMac) + TypeCast::uint64ToString(MeshUtilityFunctions::randomUint64()) + TypeCast::uint64ToString(MeshUtilityFunctions::randomUint64()); - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; _ongoingPeerRequestNonce = requestNonce; _ongoingPeerRequester = this; _reciprocalPeerRequestConfirmation = false; @@ -1732,12 +1740,11 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne verboseModePrint(String(F("Sending encrypted connection request to: ")) + TypeCast::macToString(peerMac)); - if(espnowSendToNode(requestMessage, peerMac, 'P') == TS_TRANSMISSION_COMPLETE) + if(espnowSendToNode(requestMessage, peerMac, 'P') == TransmissionStatusType::TRANSMISSION_COMPLETE) { - uint32_t startTime = millis(); - + ExpiringTimeTracker requestTimeout([](){ return getEncryptionRequestTimeout(); }); // _ongoingPeerRequestNonce is set to "" when a peer confirmation response from the mac is received - while(millis() - startTime < getEncryptionRequestTimeout() && !_ongoingPeerRequestNonce.isEmpty()) + while(!requestTimeout && !_ongoingPeerRequestNonce.isEmpty()) { // For obvious reasons dividing by exactly 10 is a good choice. ExpiringTimeTracker maxDurationTracker = ExpiringTimeTracker(getEncryptionRequestTimeout()/10); @@ -1749,15 +1756,15 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne if(!_ongoingPeerRequestNonce.isEmpty()) { // If nonce != "" we only received the basic connection info, so the pairing process is incomplete - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; _ongoingPeerRequestNonce.clear(); } else if(encryptedConnectionEstablished(_ongoingPeerRequestResult)) { - if(_ongoingPeerRequestResult == ECS_CONNECTION_ESTABLISHED) + if(_ongoingPeerRequestResult == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) // Give the builder a chance to update the message requestMessage = encryptionRequestBuilder(requestNonce, existingTimeTracker); - else if(_ongoingPeerRequestResult == ECS_SOFT_LIMIT_CONNECTION_ESTABLISHED) + else if(_ongoingPeerRequestResult == EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED) // We will only get a soft limit connection. Adjust future actions based on this. requestMessage = JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, getEspnowHashKey(), espnowHashKeyLength, getAutoEncryptionDuration()); @@ -1769,20 +1776,20 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne String messageBody = requestMessage.substring(messageHeaderEndIndex + 1); // If we do not get an ack within getEncryptionRequestTimeout() the peer has probably had the time to delete the temporary encrypted connection. - if(espnowSendToNode(String(FPSTR(encryptedConnectionVerificationHeader)) + requestMessage, peerMac, 'P') == TS_TRANSMISSION_COMPLETE - && millis() - _ongoingPeerRequestEncryptionStart < getEncryptionRequestTimeout()) + if(espnowSendToNode(String(FPSTR(encryptedConnectionVerificationHeader)) + requestMessage, peerMac, 'P') == TransmissionStatusType::TRANSMISSION_COMPLETE + && !_ongoingPeerRequestEncryptionTimeout) { EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerMac); if(!encryptedConnection) { assert(encryptedConnection && String(F("requestEncryptedConnectionKernel cannot find an encrypted connection!"))); // requestEncryptedConnectionRemoval received. - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } else if(encryptedConnection->removalScheduled() || (encryptedConnection->temporary() && encryptedConnection->temporary()->expired())) { // Could possibly be caused by a simultaneous temporary peer request from the peer. - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } else { @@ -1805,13 +1812,13 @@ encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnectionKerne else { assert(false && String(F("Unknown messageHeader during encrypted connection finalization!"))); - _ongoingPeerRequestResult = ECS_API_CALL_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::API_CALL_FAILED; } } } else { - _ongoingPeerRequestResult = ECS_REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; } } @@ -1849,20 +1856,20 @@ String EspnowMeshBackend::flexibleEncryptionRequestBuilder(const uint32_t minDur return createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, hashKey, espnowHashKeyLength, connectionDuration); } -encrypted_connection_status_t EspnowMeshBackend::requestEncryptedConnection(uint8_t *peerMac) +EncryptedConnectionStatus EspnowMeshBackend::requestEncryptedConnection(uint8_t *peerMac) { using namespace std::placeholders; return requestEncryptedConnectionKernel(peerMac, std::bind(defaultEncryptionRequestBuilder, FPSTR(EspnowProtocolInterpreter::encryptionRequestHeader), 0, getEspnowHashKey(), _1, _2)); } -encrypted_connection_status_t EspnowMeshBackend::requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs) +EncryptedConnectionStatus EspnowMeshBackend::requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs) { using namespace std::placeholders; return requestEncryptedConnectionKernel(peerMac, std::bind(defaultEncryptionRequestBuilder, FPSTR(EspnowProtocolInterpreter::temporaryEncryptionRequestHeader), durationMs, getEspnowHashKey(), _1, _2)); } -encrypted_connection_status_t EspnowMeshBackend::requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs) +EncryptedConnectionStatus EspnowMeshBackend::requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs) { using namespace std::placeholders; return requestEncryptedConnectionKernel(peerMac, std::bind(flexibleEncryptionRequestBuilder, minDurationMs, getEspnowHashKey(), _1, _2)); @@ -1875,13 +1882,11 @@ bool EspnowMeshBackend::temporaryEncryptedConnectionToPermanent(uint8_t *peerMac temporaryConnection->removeDuration(); return true; } - else - { - return false; - } + + return false; } -encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnection(uint8_t *peerMac) +EncryptedConnectionRemovalOutcome EspnowMeshBackend::removeEncryptedConnection(uint8_t *peerMac) { auto connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections); if(connectionIterator != encryptedConnections.end()) @@ -1892,27 +1897,25 @@ encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnect // We should not remove an encrypted connection while there is a transmission in progress, since that may cause encrypted data to be sent unencrypted. // Thus when a transmission is in progress we just schedule the encrypted connection for removal, so it will be removed during the next updateTemporaryEncryptedConnections() call. connectionIterator->scheduleForRemoval(); - return ECRO_REMOVAL_SCHEDULED; + return EncryptedConnectionRemovalOutcome::REMOVAL_SCHEDULED; } else { return removeEncryptedConnectionUnprotected(peerMac); } } - else - { - // peerMac is already removed - return ECRO_REMOVAL_SUCCEEDED; - } + + // peerMac is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; } -encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator) +EncryptedConnectionRemovalOutcome EspnowMeshBackend::removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator) { connectionLogIterator connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections); return removeEncryptedConnectionUnprotected(connectionIterator, resultingIterator); } -encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator) +EncryptedConnectionRemovalOutcome EspnowMeshBackend::removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator) { assert(encryptedConnections.size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library @@ -1945,19 +1948,17 @@ encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnect deleteEntriesByMac(sentRequests, encryptedMac, true); deleteEntriesByMac(receivedRequests, encryptedMac, true); - return ECRO_REMOVAL_SUCCEEDED; + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; } else { staticVerboseModePrint(String(F("Removal failed"))); - return ECRO_REMOVAL_FAILED; + return EncryptedConnectionRemovalOutcome::REMOVAL_FAILED; } } - else - { - // connection is already removed - return ECRO_REMOVAL_SUCCEEDED; - } + + // connection is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; } template @@ -2016,7 +2017,7 @@ void EspnowMeshBackend::deleteEntriesByMac(std::mapremovalScheduled()) - return ECRO_REMOVAL_SUCCEEDED; // Removal will be completed by mutex destructorHook. + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; // Removal will be completed by mutex destructorHook. else - return ECRO_REMOVAL_REQUEST_FAILED; + return EncryptedConnectionRemovalOutcome::REMOVAL_REQUEST_FAILED; } } - else - { - // peerMac is already removed - return ECRO_REMOVAL_SUCCEEDED; - } + + // peerMac is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; } void EspnowMeshBackend::setAcceptsUnverifiedRequests(bool acceptsUnverifiedRequests) { _acceptsUnverifiedRequests = acceptsUnverifiedRequests; } @@ -2062,11 +2061,11 @@ void EspnowMeshBackend::setEncryptedConnectionsSoftLimit(uint8_t softLimit) uint8_t EspnowMeshBackend::encryptedConnectionsSoftLimit() { return _encryptedConnectionsSoftLimit; } template -typename std::vector::iterator EspnowMeshBackend::getEncryptedConnectionIterator(const uint8_t *peerMac, typename std::vector &connectionVector) +typename T::iterator EspnowMeshBackend::getEncryptedConnectionIterator(const uint8_t *peerMac, T &connectionContainer) { - typename std::vector::iterator connectionIterator = connectionVector.begin(); + typename T::iterator connectionIterator = connectionContainer.begin(); - while(connectionIterator != connectionVector.end()) + while(connectionIterator != connectionContainer.end()) { if(connectionIterator->connectedTo(peerMac)) break; @@ -2091,10 +2090,8 @@ bool EspnowMeshBackend::getEncryptedConnectionIterator(const uint8_t *peerMac, c iterator = result; return true; } - else - { - return false; - } + + return false; } bool EspnowMeshBackend::getTemporaryEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator) @@ -2106,10 +2103,8 @@ bool EspnowMeshBackend::getTemporaryEncryptedConnectionIterator(const uint8_t *p iterator = result; return true; } - else - { - return false; - } + + return false; } EncryptedConnectionLog *EspnowMeshBackend::getEncryptedConnection(const uint8_t *peerMac) @@ -2119,10 +2114,8 @@ EncryptedConnectionLog *EspnowMeshBackend::getEncryptedConnection(const uint8_t { return &(*connectionIterator); } - else - { - return nullptr; - } + + return nullptr; } EncryptedConnectionLog *EspnowMeshBackend::getTemporaryEncryptedConnection(const uint8_t *peerMac) @@ -2132,10 +2125,8 @@ EncryptedConnectionLog *EspnowMeshBackend::getTemporaryEncryptedConnection(const { return &(*connectionIterator); } - else - { - return nullptr; - } + + return nullptr; } @@ -2145,10 +2136,8 @@ uint8_t *EspnowMeshBackend::getEncryptedMac(const uint8_t *peerMac, uint8_t *res { return encryptedConnection->getEncryptedPeerMac(resultArray); } - else - { - return nullptr; - } + + return nullptr; } void EspnowMeshBackend::prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels) @@ -2164,7 +2153,7 @@ void EspnowMeshBackend::prepareForTransmission(const String &message, bool scan, } } -transmission_status_t EspnowMeshBackend::initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) +TransmissionStatusType EspnowMeshBackend::initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) { uint8_t targetBSSID[6] {0}; @@ -2180,17 +2169,17 @@ transmission_status_t EspnowMeshBackend::initiateTransmission(const String &mess return initiateTransmissionKernel(message, targetBSSID); } -transmission_status_t EspnowMeshBackend::initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID) +TransmissionStatusType EspnowMeshBackend::initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID) { uint32_t transmissionStartTime = millis(); - transmission_status_t transmissionResult = sendRequest(message, targetBSSID); + TransmissionStatusType transmissionResult = sendRequest(message, targetBSSID); uint32_t transmissionDuration = millis() - transmissionStartTime; - if(verboseMode() && transmissionResult == TS_TRANSMISSION_COMPLETE) // Avoid calculations if not required + if(verboseMode() && transmissionResult == TransmissionStatusType::TRANSMISSION_COMPLETE) // Avoid calculations if not required { totalDurationWhenSuccessful_AT += transmissionDuration; - successfulTransmissions_AT++; + ++successfulTransmissions_AT; if(transmissionDuration > maxTransmissionDuration_AT) { maxTransmissionDuration_AT = transmissionDuration; @@ -2233,7 +2222,7 @@ void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bo { for(const EspnowNetworkInfo ¤tNetwork : constConnectionQueue()) { - transmission_status_t transmissionResult = initiateTransmission(getMessage(), currentNetwork); + TransmissionStatusType transmissionResult = initiateTransmission(getMessage(), currentNetwork); latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); @@ -2245,19 +2234,19 @@ void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bo printTransmissionStatistics(); } -transmission_status_t EspnowMeshBackend::attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) +TransmissionStatusType EspnowMeshBackend::attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) { MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals); if(!mutexTracker.mutexCaptured()) { assert(false && String(F("ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } return initiateTransmission(message, recipientInfo); } -encrypted_connection_status_t EspnowMeshBackend::initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection) +EncryptedConnectionStatus EspnowMeshBackend::initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection) { assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect recipientInfo.getBSSID(targetBSSID); @@ -2269,7 +2258,7 @@ encrypted_connection_status_t EspnowMeshBackend::initiateAutoEncryptingConnectio } *existingEncryptedConnection = getEncryptedConnection(targetBSSID); - encrypted_connection_status_t connectionStatus = ECS_MAX_CONNECTIONS_REACHED_SELF; + EncryptedConnectionStatus connectionStatus = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; if(requestPermanentConnection) connectionStatus = requestEncryptedConnection(targetBSSID); @@ -2279,9 +2268,9 @@ encrypted_connection_status_t EspnowMeshBackend::initiateAutoEncryptingConnectio return connectionStatus; } -transmission_status_t EspnowMeshBackend::initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, encrypted_connection_status_t connectionStatus) +TransmissionStatusType EspnowMeshBackend::initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, EncryptedConnectionStatus connectionStatus) { - transmission_status_t transmissionResult = TS_CONNECTION_FAILED; + TransmissionStatusType transmissionResult = TransmissionStatusType::CONNECTION_FAILED; if(encryptedConnectionEstablished(connectionStatus)) { @@ -2326,7 +2315,7 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, { uint8_t currentBSSID[6] {0}; EncryptedConnectionLog *existingEncryptedConnection = nullptr; - encrypted_connection_status_t connectionStatus = initiateAutoEncryptingConnection(currentNetwork, requestPermanentConnections, currentBSSID, &existingEncryptedConnection); + EncryptedConnectionStatus connectionStatus = initiateAutoEncryptingConnection(currentNetwork, requestPermanentConnections, currentBSSID, &existingEncryptedConnection); MutexTracker innerMutexTracker = MutexTracker(_espnowTransmissionMutex); if(!innerMutexTracker.mutexCaptured()) @@ -2335,7 +2324,7 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, return; } - transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus); + TransmissionStatusType transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus); latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); @@ -2349,20 +2338,20 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, printTransmissionStatistics(); } -transmission_status_t EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection) +TransmissionStatusType EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection) { uint8_t targetBSSID[6] {0}; EncryptedConnectionLog *existingEncryptedConnection = nullptr; - encrypted_connection_status_t connectionStatus = initiateAutoEncryptingConnection(recipientInfo, requestPermanentConnection, targetBSSID, &existingEncryptedConnection); + EncryptedConnectionStatus connectionStatus = initiateAutoEncryptingConnection(recipientInfo, requestPermanentConnection, targetBSSID, &existingEncryptedConnection); MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals); if(!mutexTracker.mutexCaptured()) { assert(false && String(F("ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } - transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(message, targetBSSID, connectionStatus); + TransmissionStatusType transmissionResult = initiateAutoEncryptingTransmission(message, targetBSSID, connectionStatus); finalizeAutoEncryptingConnection(targetBSSID, existingEncryptedConnection, requestPermanentConnection); @@ -2412,7 +2401,7 @@ void EspnowMeshBackend::sendPeerRequestConfirmations(const ExpiringTimeTracker * auto timeTrackerPointer = confirmationsIterator->temporary(); assert(timeTrackerPointer); // peerRequestConfirmations should always expire and so should always have a timeTracker - if(timeTrackerPointer->timeSinceCreation() > getEncryptionRequestTimeout() + if(timeTrackerPointer->elapsedTime() > getEncryptionRequestTimeout() || (reciprocalPeerRequest && confirmationsIterator->getPeerRequestNonce() <= initialOngoingPeerRequestNonce)) { // The peer request has expired, @@ -2450,7 +2439,7 @@ void EspnowMeshBackend::sendPeerRequestConfirmations(const ExpiringTimeTracker * else if(espnowSendToNodeUnsynchronized(JsonTranslator::createEncryptionRequestHmacMessage(FPSTR(basicConnectionInfoHeader), confirmationsIterator->getPeerRequestNonce(), hashKey, espnowHashKeyLength), sendToDefaultBSSID ? defaultBSSID : unencryptedBSSID, 'C', generateMessageID(nullptr)) // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. - == TS_TRANSMISSION_COMPLETE) + == TransmissionStatusType::TRANSMISSION_COMPLETE) { // Try to add encrypted connection. If connection added send confirmation with encryptedConnection->getOwnSessionKey() as session key and C type message (won't increment key). Then proceed with next request (no need to wait for answer). if(existingEncryptedConnection) @@ -2547,7 +2536,7 @@ void EspnowMeshBackend::sendEspnowResponses(const ExpiringTimeTracker *estimated uint32_t responseIndex = 0; for(std::list::iterator responseIterator = responsesToSend.begin(); responseIterator != responsesToSend.end(); ++responseIndex) { - if(responseIterator->timeSinceCreation() > logEntryLifetimeMs()) + if(responseIterator->getTimeTracker().timeSinceCreation() > logEntryLifetimeMs()) { // If the response is older than logEntryLifetimeMs(), the corresponding request log entry has been deleted at the request sender, // so the request sender will not accept our response any more. @@ -2560,7 +2549,7 @@ void EspnowMeshBackend::sendEspnowResponses(const ExpiringTimeTracker *estimated // Note that callbacks can be called during delay time, so it is possible to receive a transmission during espnowSendToNode // (which may add an element to the responsesToSend list). if(espnowSendToNodeUnsynchronized(responseIterator->getMessage(), responseIterator->getRecipientMac(), 'A', responseIterator->getRequestID()) - == TS_TRANSMISSION_COMPLETE) + == TransmissionStatusType::TRANSMISSION_COMPLETE) { if(EspnowMeshBackend *currentEspnowRequestManager = getEspnowRequestManager()) hookOutcome = currentEspnowRequestManager->getResponseTransmittedHook()(responseIterator->getMessage(), responseIterator->getRecipientMac(), responseIndex, *currentEspnowRequestManager); @@ -2626,32 +2615,28 @@ uint8_t EspnowMeshBackend::reservedEncryptedConnections() return encryptedConnections.size(); } -espnow_connection_type_t EspnowMeshBackend::getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac) +ConnectionType EspnowMeshBackend::getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac) { if(!encryptedConnection) { - return ECT_NO_CONNECTION; + return ConnectionType::NO_CONNECTION; } - else - { - if(peerMac) - encryptedConnection->getEncryptedPeerMac(peerMac); - - if(const ExpiringTimeTracker *timeTracker = encryptedConnection->temporary()) - { - if(remainingDuration) - *remainingDuration = timeTracker->remainingDuration(); - return ECT_TEMPORARY_CONNECTION; - } - else - { - return ECT_PERMANENT_CONNECTION; - } + if(peerMac) + encryptedConnection->getEncryptedPeerMac(peerMac); + + if(const ExpiringTimeTracker *timeTracker = encryptedConnection->temporary()) + { + if(remainingDuration) + *remainingDuration = timeTracker->remainingDuration(); + + return ConnectionType::TEMPORARY_CONNECTION; } + + return ConnectionType::PERMANENT_CONNECTION; } -espnow_connection_type_t EspnowMeshBackend::getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration) +ConnectionType EspnowMeshBackend::getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration) { EncryptedConnectionLog *encryptedConnection = nullptr; @@ -2661,7 +2646,7 @@ espnow_connection_type_t EspnowMeshBackend::getConnectionInfo(uint8_t *peerMac, return getConnectionInfoHelper(encryptedConnection, remainingDuration); } -espnow_connection_type_t EspnowMeshBackend::getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration, uint8_t *peerMac) +ConnectionType EspnowMeshBackend::getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration, uint8_t *peerMac) { EncryptedConnectionLog *encryptedConnection = nullptr; @@ -2675,8 +2660,8 @@ double EspnowMeshBackend::getTransmissionFailRate() { if(_transmissionsTotal == 0) return 0; - else - return _transmissionsFailed/_transmissionsTotal; + + return _transmissionsFailed/_transmissionsTotal; } void EspnowMeshBackend::resetTransmissionFailRate() @@ -2696,21 +2681,25 @@ String EspnowMeshBackend::serializeUnencryptedConnection() String EspnowMeshBackend::serializeEncryptedConnection(const uint8_t *peerMac) { + String serializedConnection(emptyString); + EncryptedConnectionLog *encryptedConnection = nullptr; if(peerMac) encryptedConnection = getEncryptedConnection(peerMac); if(encryptedConnection) - return encryptedConnection->serialize(); - else - return emptyString; + serializedConnection = encryptedConnection->serialize(); + + return serializedConnection; } String EspnowMeshBackend::serializeEncryptedConnection(uint32_t connectionIndex) { + String serializedConnection(emptyString); + if(connectionIndex < numberOfEncryptedConnections()) - return encryptedConnections[connectionIndex].serialize(); - else - return emptyString; + serializedConnection = encryptedConnections[connectionIndex].serialize(); + + return serializedConnection; } diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h index 17ca2d26d..908cf3620 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h @@ -90,32 +90,34 @@ #include "EspnowNetworkInfo.h" #include "CryptoInterface.h" -typedef enum +namespace Espnow { - ECT_NO_CONNECTION = 0, - ECT_TEMPORARY_CONNECTION = 1, - ECT_PERMANENT_CONNECTION = 2 -} espnow_connection_type_t; +enum class ConnectionType +{ + NO_CONNECTION = 0, + TEMPORARY_CONNECTION = 1, + PERMANENT_CONNECTION = 2 +}; // A value greater than 0 means that an encrypted connection has been established. -typedef enum +enum class EncryptedConnectionStatus { - ECS_MAX_CONNECTIONS_REACHED_SELF = -3, - ECS_REQUEST_TRANSMISSION_FAILED = -2, - ECS_MAX_CONNECTIONS_REACHED_PEER = -1, - ECS_API_CALL_FAILED = 0, - ECS_CONNECTION_ESTABLISHED = 1, - ECS_SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6. -} encrypted_connection_status_t; + MAX_CONNECTIONS_REACHED_SELF = -3, + REQUEST_TRANSMISSION_FAILED = -2, + MAX_CONNECTIONS_REACHED_PEER = -1, + API_CALL_FAILED = 0, + CONNECTION_ESTABLISHED = 1, + SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6. +}; -typedef enum +enum class EncryptedConnectionRemovalOutcome { - ECRO_REMOVAL_REQUEST_FAILED = -1, - ECRO_REMOVAL_FAILED = 0, - ECRO_REMOVAL_SUCCEEDED = 1, - ECRO_REMOVAL_SCHEDULED = 2 -} encrypted_connection_removal_outcome_t; - + REMOVAL_REQUEST_FAILED = -1, + REMOVAL_FAILED = 0, + REMOVAL_SUCCEEDED = 1, + REMOVAL_SCHEDULED = 2 +}; +} /** * An alternative to standard delay(). Will continuously call performEspnowMaintenance() during the waiting time, so that the ESP-NOW node remains responsive. @@ -130,12 +132,14 @@ void espnowDelay(uint32_t durationMs); class RequestData; +using namespace Espnow; // TODO: Remove + class EspnowMeshBackend : public MeshBackendBase { protected: - typedef std::function broadcastFilterType; - typedef std::function responseTransmittedHookType; + using broadcastFilterType = std::function; + using responseTransmittedHookType = std::function; public: @@ -145,7 +149,7 @@ public: * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which * is the request string received from another node and returns the string to send back. * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which - * is the response string received from another node. Returns a transmission status code as a transmission_status_t. + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. * @param networkFilter The callback handler for deciding which WiFi networks to connect to. * @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept. * @param meshPassword The WiFi password for the mesh network. @@ -155,7 +159,7 @@ public: * @param ssidSuffix The suffix (last part) of the node SSID. * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly @@ -173,7 +177,7 @@ public: * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which * is the request string received from another node and returns the string to send back. * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which - * is the response string received from another node. Returns a transmission status code as a transmission_status_t. + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. * @param networkFilter The callback handler for deciding which WiFi networks to connect to. * @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept. * @param meshPassword The WiFi password for the mesh network. @@ -183,7 +187,7 @@ public: * @param ssidSuffix The suffix (last part) of the node SSID. * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly @@ -222,7 +226,7 @@ public: static std::vector & latestTransmissionOutcomes(); /** - * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. * The result is unique for each mesh backend. */ static bool latestTransmissionSuccessful(); @@ -285,7 +289,7 @@ public: * * @param recipientInfo The recipient information. */ - transmission_status_t attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); + TransmissionStatusType attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); /* * Will ensure that an encrypted connection exists to each target node before sending the message, @@ -317,7 +321,7 @@ public: * Transmit message to a single recipient without changing the local transmission state (apart from encrypted connections). * Will not change connectionQueue, latestTransmissionOutcomes or stored message. */ - transmission_status_t attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection = false); + TransmissionStatusType attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection = false); /** * Send a message simultaneously to all nearby nodes which have ESP-NOW activated. @@ -736,32 +740,32 @@ public: // Updates connection with current stored encrypted connection key. // At least one of the leftmost 32 bits in each of the session keys should be 1, since the key otherwise indicates the connection is unencrypted. - encrypted_connection_status_t addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey); + EncryptedConnectionStatus addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey); // Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized. // These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection. - // @param ignoreDuration Ignores any stored duration serializedConnectionState, guaranteeing that the created connection will be permanent. Returns: ECS_REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState. - encrypted_connection_status_t addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration = false); + // @param ignoreDuration Ignores any stored duration serializedConnectionState, guaranteeing that the created connection will be permanent. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState. + EncryptedConnectionStatus addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration = false); // Adds a new temporary encrypted connection, or changes the duration of an existing temporary connection (only updates keys, not duration, for existing permanent connections). // As with all these methods, changes will only take effect once the requester proves it has the ability to decrypt the session key. // At least one of the leftmost 32 bits in each of the session keys should be 1, since the key otherwise indicates the connection is unencrypted. - encrypted_connection_status_t addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration); + EncryptedConnectionStatus addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration); // Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized. // These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection. - // Uses duration argument instead of any stored duration in serializedConnectionState. Returns: ECS_REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState. - encrypted_connection_status_t addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration); + // Uses duration argument instead of any stored duration in serializedConnectionState. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState. + EncryptedConnectionStatus addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration); // If an encrypted connection to peerMac already exists, only connection duration is updated. All other settings are kept as is. Use removeEncryptedConnection/requestEncryptedConnectionRemoval first if encryption keys should be updated. // Makes sure both nodes have an encrypted connection to each other that's permanent. - encrypted_connection_status_t requestEncryptedConnection(uint8_t *peerMac); + EncryptedConnectionStatus requestEncryptedConnection(uint8_t *peerMac); // Makes sure both nodes have an encrypted connection to each other that's either permanent or has the duration specified. - encrypted_connection_status_t requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs); + EncryptedConnectionStatus requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs); // Makes sure both nodes have an encrypted connection to each other that's either permanent or has at least the duration specified. // Note that if a temporary encrypted connection already exists to a target node, this method will slightly extend the connection duration // depending on the time it takes to verify the connection to the node. - encrypted_connection_status_t requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs); - static encrypted_connection_removal_outcome_t removeEncryptedConnection(uint8_t *peerMac); - encrypted_connection_removal_outcome_t requestEncryptedConnectionRemoval(uint8_t *peerMac); + EncryptedConnectionStatus requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs); + static EncryptedConnectionRemovalOutcome removeEncryptedConnection(uint8_t *peerMac); + EncryptedConnectionRemovalOutcome requestEncryptedConnectionRemoval(uint8_t *peerMac); /** * Set whether this EspnowMeshBackend instance will accept ESP-NOW requests from unencrypted connections or not, when acting as EspnowRequestManager. @@ -824,24 +828,24 @@ public: * @param peerMac The node MAC for which to get information. Both MAC for AP interface and MAC for STA interface can be used (and will yield the same result). * Use the getEncryptedMac method or the indexed based getConnectionInfo if there is a need to find the actual encrypted interface. * @param remainingDuration An optional pointer to a uint32_t variable. - * If supplied and the connection type is ECT_TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. + * If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. * Otherwise the variable value is not modified. - * @return The espnow_connection_type_t of the connection with peerMac. + * @return The ConnectionType of the connection with peerMac. */ - static espnow_connection_type_t getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr); + static ConnectionType getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr); /** * Get information about any current ESP-NOW connection with another node. * * @param connectionIndex The connection index of the node for which to get information. Valid values are limited by numberOfEncryptedConnections(). * @param remainingDuration An optional pointer to a uint32_t variable. - * If supplied and the connection type is ECT_TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. + * If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. * Otherwise the variable value is not modified. * @param peerMac An optional pointer to an uint8_t array with at least size 6. It will be filled with the MAC of the encrypted peer interface if an encrypted connection exists. * Otherwise the array is not modified. - * @return The espnow_connection_type_t of the connection given by connectionIndex. + * @return The ConnectionType of the connection given by connectionIndex. */ - static espnow_connection_type_t getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr); + static ConnectionType getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr); /** * @return The proportion of ESP-NOW requests made by this node that have failed, since power on or latest reset. @@ -858,7 +862,7 @@ protected: static std::vector _connectionQueue; static std::vector _latestTransmissionOutcomes; - typedef std::vector::iterator connectionLogIterator; + using connectionLogIterator = std::vector::iterator; static connectionLogIterator connectionLogEndIterator(); static const uint8_t broadcastMac[6]; @@ -866,7 +870,7 @@ protected: bool activateEspnow(); - static bool encryptedConnectionEstablished(encrypted_connection_status_t connectionStatus); + static bool encryptedConnectionEstablished(EncryptedConnectionStatus connectionStatus); /* * Note that ESP-NOW is not perfect and in rare cases messages may be dropped. @@ -906,8 +910,8 @@ protected: // Consider using getScheduledResponseRecipient and similar methods for this preparation. // Should only be used when there is no transmissions in progress. In practice when _espnowTransmissionMutex is free. // @param resultingIterator Will be set to the iterator position after the removed element, if an element to remove was found. Otherwise no change will occur. - static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator = nullptr); - static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator); + static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator = nullptr); + static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator); /** * Set the MAC address considered to be the sender of the most recently received ESP-NOW request, response or broadcast. @@ -935,17 +939,17 @@ protected: /** * Will be true if a transmission initiated by a public method is in progress. */ - static bool _espnowTransmissionMutex; + static std::shared_ptr _espnowTransmissionMutex; /** * Will be true when the connectionQueue should not be modified. */ - static bool _espnowConnectionQueueMutex; + static std::shared_ptr _espnowConnectionQueueMutex; /** * Will be true when no responsesToSend element should be removed. */ - static bool _responsesToSendMutex; + static std::shared_ptr _responsesToSendMutex; /** * Check if there is an ongoing ESP-NOW transmission in the library. Used to avoid interrupting transmissions. @@ -955,8 +959,8 @@ protected: static bool transmissionInProgress(); enum class macAndType_td : uint64_t {}; - typedef uint64_t messageID_td; - typedef uint64_t peerMac_td; + using messageID_td = uint64_t; + using peerMac_td = uint64_t; static macAndType_td createMacAndTypeValue(uint64_t uint64Mac, char messageType); static uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue); @@ -985,19 +989,19 @@ protected: * @return The transmission status for the transmission. */ // Send a message to the node having targetBSSID as mac, changing targetBSSID to the mac of the encrypted connection if it exists and ensuring such an encrypted connection is synchronized. - static transmission_status_t espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance = nullptr); + static TransmissionStatusType espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance = nullptr); // Send a message using exactly the arguments given, without consideration for any encrypted connections. - static transmission_status_t espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance = nullptr); + static TransmissionStatusType espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance = nullptr); - transmission_status_t sendRequest(const String &message, const uint8_t *targetBSSID); - transmission_status_t sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID); + TransmissionStatusType sendRequest(const String &message, const uint8_t *targetBSSID); + TransmissionStatusType sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID); private: EspnowMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, broadcastFilterType broadcastFilter, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel); - typedef std::function encryptionRequestBuilderType; + using encryptionRequestBuilderType = std::function; static String defaultEncryptionRequestBuilder(const String &requestHeader, const uint32_t durationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker); static String flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker); @@ -1055,14 +1059,14 @@ private: static EncryptedConnectionLog *getEncryptedConnection(const uint8_t *peerMac); static EncryptedConnectionLog *getTemporaryEncryptedConnection(const uint8_t *peerMac); - //@return iterator to connection in connectionVector, or connectionVector.end() if element not found + //@return iterator to connection in connectionContainer, or connectionContainer.end() if element not found template - static typename std::vector::iterator getEncryptedConnectionIterator(const uint8_t *peerMac, typename std::vector &connectionVector); + static typename T::iterator getEncryptedConnectionIterator(const uint8_t *peerMac, T &connectionContainer); static bool getEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator); // @return true if an encrypted connection to peerMac is found and the found connection is temporary. Only changes iterator if true is returned. static bool getTemporaryEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator); - static espnow_connection_type_t getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr); + static ConnectionType getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr); // Should only be used when there is no transmissions in progress, so it is safe to remove encrypted connections. In practice when _espnowTransmissionMutex is free. // @param scheduledRemovalOnly If true, only deletes encrypted connections where removalScheduled() is true. This means only connections which have been requested for removal will be deleted, @@ -1072,6 +1076,9 @@ private: template static void deleteExpiredLogEntries(std::map, T> &logEntries, uint32_t maxEntryLifetimeMs); + template + static void deleteExpiredLogEntries(std::map, TimeTracker> &logEntries, uint32_t maxEntryLifetimeMs); + static void deleteExpiredLogEntries(std::map, RequestData> &logEntries, uint32_t requestLifetimeMs, uint32_t broadcastLifetimeMs); template @@ -1082,7 +1089,6 @@ private: static uint32_t _encryptionRequestTimeoutMs; - static uint32_t _timeOfLastLogClear; static uint32_t _criticalHeapLevel; static uint32_t _criticalHeapLevelBuffer; @@ -1096,8 +1102,7 @@ private: static String _ongoingPeerRequestNonce; static uint8_t _ongoingPeerRequestMac[6]; static EspnowMeshBackend *_ongoingPeerRequester; - static encrypted_connection_status_t _ongoingPeerRequestResult; - static uint32_t _ongoingPeerRequestEncryptionStart; + static EncryptedConnectionStatus _ongoingPeerRequestResult; static bool _reciprocalPeerRequestConfirmation; template @@ -1119,7 +1124,7 @@ private: uint8_t _senderAPMac[6] = {0}; bool _receivedEncryptedTransmission = false; - static bool _espnowSendToNodeMutex; + static std::shared_ptr _espnowSendToNodeMutex; static uint8_t _transmissionTargetBSSID[6]; static void storeSentRequest(const uint64_t targetBSSID, const uint64_t messageID, const RequestData &requestData); @@ -1148,9 +1153,9 @@ private: * @param encryptionRequestBuilder A function which is responsible for constructing the request message to send. * Called twice when the request is successful. First to build the initial request message and then to build the connection verification message. * The request message should typically be of the form: JsonTranslator::createEncryptionRequestIntro() + JsonTranslator::createEncryptionRequestEnding(). - * @return The ultimate status of the requested encrypted connection, as encrypted_connection_status_t. + * @return The ultimate status of the requested encrypted connection, as EncryptedConnectionStatus. */ - encrypted_connection_status_t requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder); + EncryptedConnectionStatus requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder); /** * Generate a new message ID to be used when making a data transmission. The generated ID will be different depending on whether an encrypted connection exists or not. @@ -1170,12 +1175,12 @@ private: static uint64_t createSessionKey(); void prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels); - transmission_status_t initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); - transmission_status_t initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID); + TransmissionStatusType initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); + TransmissionStatusType initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID); void printTransmissionStatistics(); - encrypted_connection_status_t initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection); - transmission_status_t initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, encrypted_connection_status_t connectionStatus); + EncryptedConnectionStatus initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection); + TransmissionStatusType initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, EncryptedConnectionStatus connectionStatus); void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, bool requestPermanentConnection); // Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter diff --git a/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h index 42b56232b..8a227669c 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h +++ b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h @@ -45,7 +45,7 @@ namespace EspnowProtocolInterpreter constexpr char basicConnectionInfoHeader[] PROGMEM = "BasicCI:"; // Basic connection info constexpr char encryptedConnectionInfoHeader[] PROGMEM = "EncryptedCI:"; // Encrypted connection info constexpr char softLimitEncryptedConnectionInfoHeader[] PROGMEM = "SLEncryptedCI:"; // Soft limit encrypted connection info - constexpr char maxConnectionsReachedHeader[] PROGMEM = "ECS_MAX_CONNECTIONS_REACHED_PEER:"; + constexpr char maxConnectionsReachedHeader[] PROGMEM = "MAX_CONNECTIONS_REACHED_PEER:"; constexpr char encryptedConnectionVerificationHeader[] PROGMEM = "ECVerified:"; // Encrypted connection verified constexpr char encryptedConnectionRemovalRequestHeader[] PROGMEM = "RemoveEC:"; // Remove encrypted connection diff --git a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp index 73b6d3214..5151c0bc5 100644 --- a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp +++ b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp @@ -24,36 +24,101 @@ #include "ExpiringTimeTracker.h" -ExpiringTimeTracker::ExpiringTimeTracker(uint32_t duration, uint32_t creationTimeMs) : - TimeTracker(creationTimeMs), _duration(duration) -{ } +ExpiringTimeTracker::ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs) : + timeoutTemplate(0) +{ + setDuration(duration); + _start = creationTimeMs; +} + +ExpiringTimeTracker::ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs) : + timeoutTemplate(0) +{ + setDuration(durationCalculator); + _start = creationTimeMs; +} uint32_t ExpiringTimeTracker::duration() const { - return _duration; + if(useCalculator) + return _durationCalculator(); + + return getTimeout(); } -void ExpiringTimeTracker::setRemainingDuration(uint32_t remainingDuration) +IRAM_ATTR // called from ISR +void ExpiringTimeTracker::setTimeout(const uint32_t newUserTimeout) { - _duration = timeSinceCreation() + remainingDuration; + _timeout = newUserTimeout; + _neverExpires = (newUserTimeout > timeMax()); // newUserTimeout < 0 is always false for uint32_t +} + +void ExpiringTimeTracker::setDuration(const uint32_t duration) +{ + setTimeout(duration); + useCalculator = false; +} + +void ExpiringTimeTracker::setDuration(const calculatorType durationCalculator) +{ + _durationCalculator = durationCalculator; + useCalculator = true; +} + +void ExpiringTimeTracker::setRemainingDuration(const uint32_t remainingDuration) +{ + setDuration(elapsedTime() + remainingDuration); +} + +void ExpiringTimeTracker::setRemainingDuration(const calculatorType remainingDurationCalculator) +{ + uint32_t currentElapsedTime = elapsedTime(); + setDuration([remainingDurationCalculator, currentElapsedTime](){ return currentElapsedTime + remainingDurationCalculator(); }); } uint32_t ExpiringTimeTracker::remainingDuration() const { - uint32_t remainingDuration = duration() - timeSinceCreation(); + uint32_t remainingDuration = 0; - if(expired()) + if(!expired()) // If expired, overflow will probably occur for remainingDuration calculation. { - // Overflow probably occured for remainingDuration calculation. - return 0; - } - else - { - return remainingDuration; + remainingDuration = duration() - elapsedTime(); } + + return remainingDuration; +} + +uint32_t ExpiringTimeTracker::elapsedTime() const +{ + return millis() - _start; } bool ExpiringTimeTracker::expired() const -{ - return timeSinceCreation() >= duration(); +{ + if(useCalculator) + return elapsedTime() >= duration(); + + return expiredOneShot(); +} + +void ExpiringTimeTracker::reset() +{ + timeoutTemplate::reset(); +} + +void ExpiringTimeTracker::reset(const uint32_t newDuration) +{ + setDuration(newDuration); + ExpiringTimeTracker::reset(); +} + +void ExpiringTimeTracker::reset(const calculatorType newDurationCalculator) +{ + setDuration(newDurationCalculator); + ExpiringTimeTracker::reset(); +} + +ExpiringTimeTracker::operator bool() const +{ + return ExpiringTimeTracker::expired(); } diff --git a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h index 32fdd9440..1f9cd636f 100644 --- a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h +++ b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h @@ -25,24 +25,54 @@ #ifndef __EXPIRINGTIMETRACKER_H__ #define __EXPIRINGTIMETRACKER_H__ -#include "TimeTracker.h" #include +#include -class ExpiringTimeTracker : public TimeTracker { +class ExpiringTimeTracker : private esp8266::polledTimeout::oneShotMs { public: - ~ExpiringTimeTracker() override = default; + using calculatorType = std::function; - ExpiringTimeTracker(uint32_t duration, uint32_t creationTimeMs = millis()); + virtual ~ExpiringTimeTracker() = default; + + ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs = millis()); + ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs = millis()); + uint32_t duration() const; - void setRemainingDuration(uint32_t remainingDuration); + void setDuration(const uint32_t duration); + void setDuration(const calculatorType durationCalculator); + uint32_t remainingDuration() const; + + /** + * Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset. + * Note that reset() will use this new duration, including the saved elapsedTime(). + */ + void setRemainingDuration(const uint32_t remainingDuration); + + /** + * Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset. + * Note that reset() will use this new duration, including the saved elapsedTime(). + */ + void setRemainingDuration(const calculatorType remainingDurationCalculator); + + /** + * Get the time since the ExpiringTimeTracker instance creation or the last reset(), whichever is more recent. + */ + uint32_t elapsedTime() const; bool expired() const; + void reset(); + void reset(const uint32_t newDuration); + void reset(const calculatorType newDurationCalculator); + explicit operator bool() const; private: - uint32_t _duration; + bool useCalculator = false; + calculatorType _durationCalculator; + + void setTimeout(const uint32_t newUserTimeout); }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp index 9b0280938..1f88dadf5 100644 --- a/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp @@ -40,13 +40,16 @@ char FloodingMesh::_metadataDelimiter = 23; void floodingMeshDelay(uint32_t durationMs) { - uint32_t startingTime = millis(); - - while(millis() - startingTime < durationMs) + ExpiringTimeTracker timeout(durationMs); + + do { + // We want to delay before performMeshMaintenance() so background tasks can be managed first. + // Initial while combined with YieldAndDelayMs polledTimeout::YieldPolicy is not suitable since the delay then occurs before evaluating the condition (meaning durationMs = 1 never executes the loop interior). delay(1); FloodingMesh::performMeshMaintenance(); } + while(!timeout); } FloodingMesh::FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::espnowEncryptedConnectionKeyLength], @@ -428,9 +431,9 @@ String FloodingMesh::_defaultRequestHandler(const String &request, MeshBackendBa * @param meshInstance The MeshBackendBase instance that called the function. * @return The status code resulting from the response, as an int */ -transmission_status_t FloodingMesh::_defaultResponseHandler(const String &response, MeshBackendBase &meshInstance) +TransmissionStatusType FloodingMesh::_defaultResponseHandler(const String &response, MeshBackendBase &meshInstance) { - transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; getEspnowMeshBackend().warningPrint(String(F("WARNING! Response to FloodingMesh broadcast received, but none is expected!"))); @@ -503,26 +506,22 @@ bool FloodingMesh::_defaultBroadcastFilter(String &firstTransmission, EspnowMesh { return false; // Broadcast is for another mesh network } - else - { - int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1); - - if(messageIDEndIndex == -1) - return false; // metadataDelimiter not found - uint64_t messageID = TypeCast::stringToUint64(firstTransmission.substring(metadataEndIndex + 1, messageIDEndIndex)); + int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1); - if(insertPreliminaryMessageID(messageID)) - { - // Add broadcast identifier to stored message and mark as accepted broadcast. - firstTransmission = String(metadataDelimiter()) + firstTransmission; - return true; - } - else - { - return false; // Broadcast has already been received the maximum number of times - } + if(messageIDEndIndex == -1) + return false; // metadataDelimiter not found + + uint64_t messageID = TypeCast::stringToUint64(firstTransmission.substring(metadataEndIndex + 1, messageIDEndIndex)); + + if(insertPreliminaryMessageID(messageID)) + { + // Add broadcast identifier to stored message and mark as accepted broadcast. + firstTransmission = String(metadataDelimiter()) + firstTransmission; + return true; } + + return false; // Broadcast has already been received the maximum number of times } /** diff --git a/libraries/ESP8266WiFiMesh/src/FloodingMesh.h b/libraries/ESP8266WiFiMesh/src/FloodingMesh.h index b35e828b6..71c662178 100644 --- a/libraries/ESP8266WiFiMesh/src/FloodingMesh.h +++ b/libraries/ESP8266WiFiMesh/src/FloodingMesh.h @@ -27,7 +27,6 @@ #include "EspnowMeshBackend.h" #include -#include #include /** @@ -45,8 +44,8 @@ class FloodingMesh { protected: - typedef std::function messageHandlerType; - typedef std::unordered_map::iterator messageQueueElementType; + using messageHandlerType = std::function; + using messageQueueElementType = std::map::iterator; public: @@ -61,7 +60,7 @@ public: * @param ssidSuffix The suffix (last part) of the node SSID. * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly @@ -83,7 +82,7 @@ public: * @param ssidSuffix The suffix (last part) of the node SSID. * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly @@ -321,14 +320,14 @@ private: uint8_t _originMac[6] = {0}; - std::unordered_map _messageIDs = {}; + std::map _messageIDs = {}; std::queue _messageIdOrder = {}; std::list> _forwardingBacklog = {}; String _macIgnoreList; String _defaultRequestHandler(const String &request, MeshBackendBase &meshInstance); - transmission_status_t _defaultResponseHandler(const String &response, MeshBackendBase &meshInstance); + TransmissionStatusType _defaultResponseHandler(const String &response, MeshBackendBase &meshInstance); void _defaultNetworkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance); bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance); diff --git a/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp b/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp new file mode 100644 index 000000000..8bda14835 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (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. + */ + +#include "HeapMonitor.h" + +HeapMonitor::HeapMonitor(const uint32_t criticalHeapLevel, const uint32_t criticalHeapLevelBuffer) : + _criticalHeapLevel(criticalHeapLevel), _criticalHeapLevelBuffer(criticalHeapLevelBuffer) +{ } + +void HeapMonitor::setCriticalHeapLevel(const uint32_t freeHeapInBytes) +{ + _criticalHeapLevel = freeHeapInBytes; +} + +uint32_t HeapMonitor::getCriticalHeapLevel() const +{ + return _criticalHeapLevel; +} + +void HeapMonitor::setCriticalHeapLevelBuffer(const uint32_t bufferInBytes) +{ + _criticalHeapLevelBuffer = bufferInBytes; +} + +uint32_t HeapMonitor::getCriticalHeapLevelBuffer() const +{ + return _criticalHeapLevelBuffer; +} + +HeapMonitor::HeapStatus HeapMonitor::getHeapStatus() const +{ + HeapStatus heapStatus = HeapStatus::NOMINAL; + + uint32_t freeHeap = ESP.getFreeHeap(); + + if(freeHeap <= getCriticalHeapLevel()) + heapStatus = HeapStatus::CRITICAL; + else if(freeHeap <= getCriticalHeapLevel() + getCriticalHeapLevelBuffer()) + heapStatus = HeapStatus::LIMITED; + + return heapStatus; +} diff --git a/libraries/ESP8266WiFiMesh/src/HeapMonitor.h b/libraries/ESP8266WiFiMesh/src/HeapMonitor.h new file mode 100644 index 000000000..44550e30d --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/HeapMonitor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (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 __ESPHEAPMONITOR_H__ +#define __ESPHEAPMONITOR_H__ + +#include + +class HeapMonitor { + +public: + + enum class HeapStatus + { + NOMINAL = 0, + LIMITED = 1, + CRITICAL = 2 + }; + + HeapMonitor(const uint32_t criticalHeapLevel, const uint32_t criticalHeapLevelBuffer); + + virtual ~HeapMonitor() = default; + + /** + * Set the maximum free heap level in bytes within which free heap size is considered critical. + */ + void setCriticalHeapLevel(const uint32_t freeHeapInBytes); + uint32_t getCriticalHeapLevel() const; + + /** + * Set the buffer of the critical heap level, within which free heap size is considered limited. + */ + void setCriticalHeapLevelBuffer(const uint32_t bufferInBytes); + uint32_t getCriticalHeapLevelBuffer() const; + + HeapStatus getHeapStatus() const; + +private: + + uint32_t _criticalHeapLevel; + uint32_t _criticalHeapLevelBuffer; + +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp index aafc45ab0..33ed8b565 100644 --- a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp +++ b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp @@ -26,11 +26,11 @@ namespace TypeCast = MeshTypeConversionFunctions; MeshBackendBase *MeshBackendBase::apController = nullptr; -bool MeshBackendBase::_scanMutex = false; +std::shared_ptr MeshBackendBase::_scanMutex = std::make_shared(false); bool MeshBackendBase::_printWarnings = true; -MeshBackendBase::MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, mesh_backend_t classType) +MeshBackendBase::MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, MeshBackendType classType) { setRequestHandler(requestHandler); setResponseHandler(responseHandler); @@ -43,12 +43,12 @@ MeshBackendBase::~MeshBackendBase() deactivateControlledAP(); } -void MeshBackendBase::setClassType(mesh_backend_t classType) +void MeshBackendBase::setClassType(MeshBackendType classType) { _classType = classType; } -mesh_backend_t MeshBackendBase::getClassType() {return _classType;} +MeshBackendType MeshBackendBase::getClassType() {return _classType;} void MeshBackendBase::activateAP() { @@ -115,7 +115,9 @@ bool MeshBackendBase::isAPController() void MeshBackendBase::setWiFiChannel(uint8 newWiFiChannel) { - assert(1 <= newWiFiChannel && newWiFiChannel <= 13); + wifi_country_t wifiCountry; + wifi_get_country(&wifiCountry); // Note: Should return 0 on success and -1 on failure, but always seems to return 1. Possibly broken API. Channels 1 to 13 are the default limits. + assert(wifiCountry.schan <= newWiFiChannel && newWiFiChannel <= wifiCountry.schan + wifiCountry.nchan - 1); _meshWiFiChannel = newWiFiChannel; @@ -248,10 +250,10 @@ bool MeshBackendBase::latestTransmissionSuccessfulBase(const std::vector requestHandlerType; - typedef std::function responseHandlerType; - typedef std::function networkFilterType; - typedef std::function transmissionOutcomesUpdateHookType; + using requestHandlerType = std::function ; + using responseHandlerType = std::function; + using networkFilterType = std::function; + using transmissionOutcomesUpdateHookType = std::function; public: - MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, mesh_backend_t classType); + MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, MeshBackendType classType); virtual ~MeshBackendBase(); @@ -109,13 +109,13 @@ public: * Will also change the WiFi channel for the active AP (via an AP restart) * if this MeshBackendBase instance is the current AP controller and it is possible to change channel. * - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several MeshBackendBase instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one MeshBackendBase instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly * make it impossible for other stations to detect the APs whose WiFi channels have changed. * - * @param newWiFiChannel The WiFi channel to change to. Valid values are integers from 1 to 13. + * @param newWiFiChannel The WiFi channel to change to. Valid values are determined by wifi_get_country, usually integers from 1 to 11 or 1 to 13. * */ void setWiFiChannel(uint8 newWiFiChannel); @@ -284,14 +284,14 @@ public: */ static void warningPrint(const String &stringToPrint, bool newline = true); - mesh_backend_t getClassType(); + MeshBackendType getClassType(); protected: /** * @param latestTransmissionOutcomes The transmission outcomes vector to check. * - * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. */ static bool latestTransmissionSuccessfulBase(const std::vector &latestTransmissionOutcomes); @@ -310,13 +310,13 @@ protected: */ virtual void deactivateAPHook(); - void setClassType(mesh_backend_t classType); + void setClassType(MeshBackendType classType); - static bool _scanMutex; + static std::shared_ptr _scanMutex; private: - mesh_backend_t _classType; + MeshBackendType _classType; static MeshBackendBase *apController; diff --git a/libraries/ESP8266WiFiMesh/src/MessageData.cpp b/libraries/ESP8266WiFiMesh/src/MessageData.cpp index 721792433..6e646f303 100644 --- a/libraries/ESP8266WiFiMesh/src/MessageData.cpp +++ b/libraries/ESP8266WiFiMesh/src/MessageData.cpp @@ -28,15 +28,15 @@ #include MessageData::MessageData(String &message, uint8_t transmissionsRemaining, uint32_t creationTimeMs) : - TimeTracker(creationTimeMs) + _timeTracker(creationTimeMs) { _transmissionsExpected = transmissionsRemaining + 1; _totalMessage += message; - _transmissionsReceived++; + ++_transmissionsReceived; } MessageData::MessageData(uint8_t *initialTransmission, uint8_t transmissionLength, uint32_t creationTimeMs) : - TimeTracker(creationTimeMs) + _timeTracker(creationTimeMs) { _transmissionsExpected = EspnowProtocolInterpreter::espnowGetTransmissionsRemaining(initialTransmission) + 1; addToMessage(initialTransmission, transmissionLength); @@ -49,7 +49,7 @@ bool MessageData::addToMessage(uint8_t *transmission, uint8_t transmissionLength String message = EspnowProtocolInterpreter::espnowGetMessageContent(transmission, transmissionLength); assert(message.length() <= EspnowMeshBackend::getMaxMessageBytesPerTransmission()); // Should catch some cases where transmission is not null terminated. _totalMessage += message; - _transmissionsReceived++; + ++_transmissionsReceived; return true; } @@ -75,3 +75,5 @@ String MessageData::getTotalMessage() { return _totalMessage; } + +const TimeTracker &MessageData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/MessageData.h b/libraries/ESP8266WiFiMesh/src/MessageData.h index 7dc5adac2..4b02fa295 100644 --- a/libraries/ESP8266WiFiMesh/src/MessageData.h +++ b/libraries/ESP8266WiFiMesh/src/MessageData.h @@ -28,7 +28,7 @@ #include "TimeTracker.h" #include -class MessageData : public TimeTracker { +class MessageData { public: @@ -43,9 +43,11 @@ public: uint8_t getTransmissionsExpected(); uint8_t getTransmissionsRemaining(); String getTotalMessage(); + const TimeTracker &getTimeTracker() const; private: + TimeTracker _timeTracker; uint8_t _transmissionsReceived = 0; uint8_t _transmissionsExpected; String _totalMessage; diff --git a/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp b/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp index c864ea76a..e6d2a6302 100644 --- a/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp +++ b/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp @@ -24,19 +24,19 @@ #include "MutexTracker.h" -bool MutexTracker::_captureBan = false; +std::shared_ptr MutexTracker::_captureBan = std::make_shared(false); -bool &MutexTracker::captureBan() +std::shared_ptr MutexTracker::captureBan() { return _captureBan; } -MutexTracker::MutexTracker(bool &mutexToCapture) +MutexTracker::MutexTracker(const std::shared_ptr &mutexToCapture) { attemptMutexCapture(mutexToCapture); } -MutexTracker::MutexTracker(bool &mutexToCapture, std::function destructorHook) : MutexTracker(mutexToCapture) +MutexTracker::MutexTracker(const std::shared_ptr &mutexToCapture, const std::function destructorHook) : MutexTracker(mutexToCapture) { _destructorHook = destructorHook; } @@ -47,12 +47,25 @@ MutexTracker::~MutexTracker() _destructorHook(); } -bool MutexTracker::mutexCaptured() +bool MutexTracker::mutexFree(const std::shared_ptr &mutex) { - if(_capturedMutex) + if(mutex != nullptr && !(*mutex)) return true; - else - return false; + + return false; +} + +bool MutexTracker::mutexCaptured(const std::shared_ptr &mutex) +{ + if(mutex != nullptr && (*mutex)) + return true; + + return false; +} + +bool MutexTracker::mutexCaptured() const +{ + return mutexCaptured(_capturedMutex); } void MutexTracker::releaseMutex() @@ -60,20 +73,18 @@ void MutexTracker::releaseMutex() if(mutexCaptured()) { *_capturedMutex = false; - _capturedMutex = nullptr; + _capturedMutex.reset(); } } -bool MutexTracker::attemptMutexCapture(bool &mutexToCapture) +bool MutexTracker::attemptMutexCapture(const std::shared_ptr &mutexToCapture) { - if(!captureBan() && !mutexToCapture) + if(mutexFree(captureBan()) && mutexFree(mutexToCapture)) { - _capturedMutex = &mutexToCapture; + _capturedMutex = mutexToCapture; *_capturedMutex = true; return true; } - else - { - return false; - } + + return false; } diff --git a/libraries/ESP8266WiFiMesh/src/MutexTracker.h b/libraries/ESP8266WiFiMesh/src/MutexTracker.h index 6e7026771..d28d26695 100644 --- a/libraries/ESP8266WiFiMesh/src/MutexTracker.h +++ b/libraries/ESP8266WiFiMesh/src/MutexTracker.h @@ -26,6 +26,7 @@ #define __MUTEXTRACKER_H__ #include +#include /** * A SLIM (Scope LImited Manager)/Scope-Bound Resource Management/RAII class to manage the state of a mutex. @@ -39,23 +40,23 @@ class MutexTracker * Set to false by default. * captureBan can be managed by MutexTracker like any other mutex. */ - static bool &captureBan(); + static std::shared_ptr captureBan(); /** * Attempts to capture the mutex. Use the mutexCaptured() method to check success. */ - MutexTracker(bool &mutexToCapture); + MutexTracker(const std::shared_ptr &mutexToCapture); /** * Attempts to capture the mutex. Use the mutexCaptured() method to check success. * * @param destructorHook A function to hook into the MutexTracker destructor. Will be called when the MutexTracker instance is being destroyed, after the mutex has been released. */ - MutexTracker(bool &mutexToCapture, std::function destructorHook); + MutexTracker(const std::shared_ptr &mutexToCapture, const std::function destructorHook); ~MutexTracker(); - bool mutexCaptured(); + bool mutexCaptured() const; /** * Set the mutex free to roam the binary plains, giving new MutexTrackers a chance to capture it. @@ -64,17 +65,20 @@ class MutexTracker private: - static bool _captureBan; + static std::shared_ptr _captureBan; - bool *_capturedMutex = nullptr; + std::shared_ptr _capturedMutex; std::function _destructorHook = [](){ }; + static bool mutexFree(const std::shared_ptr &mutex); + static bool mutexCaptured(const std::shared_ptr &mutex); + /** * Attempt to capture the mutex. * - * @return True if mutex was caught (meaning no other instance is holding the mutex). False otherwise. + * @return True if mutex was caught (meaning it exists and no other instance is holding the mutex). False otherwise. */ - bool attemptMutexCapture(bool &mutexToCapture); + bool attemptMutexCapture(const std::shared_ptr &mutexToCapture); }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp index 2cfed766e..16c2855a0 100644 --- a/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp @@ -40,7 +40,7 @@ void NetworkInfoBase::storeBSSID(const uint8_t newBSSID[6]) _BSSID = _bssidArray; } - for(int i = 0; i < 6; i++) + for(int i = 0; i < 6; ++i) { _BSSID[i] = newBSSID[i]; } diff --git a/libraries/ESP8266WiFiMesh/src/RequestData.cpp b/libraries/ESP8266WiFiMesh/src/RequestData.cpp index a45b46051..dd9dff257 100644 --- a/libraries/ESP8266WiFiMesh/src/RequestData.cpp +++ b/libraries/ESP8266WiFiMesh/src/RequestData.cpp @@ -25,8 +25,9 @@ #include "RequestData.h" RequestData::RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs) : - TimeTracker(creationTimeMs), _meshInstance(meshInstance) + _timeTracker(creationTimeMs), _meshInstance(meshInstance) { } -void RequestData::setMeshInstance(EspnowMeshBackend &meshInstance) { _meshInstance = meshInstance; } -EspnowMeshBackend &RequestData::getMeshInstance() { return _meshInstance; } +void RequestData::setMeshInstance(const EspnowMeshBackend &meshInstance) { _meshInstance = meshInstance; } +EspnowMeshBackend &RequestData::getMeshInstance() const { return _meshInstance; } +const TimeTracker &RequestData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/RequestData.h b/libraries/ESP8266WiFiMesh/src/RequestData.h index 77f373245..70b4a1fd1 100644 --- a/libraries/ESP8266WiFiMesh/src/RequestData.h +++ b/libraries/ESP8266WiFiMesh/src/RequestData.h @@ -30,17 +30,19 @@ class EspnowMeshBackend; -class RequestData : public TimeTracker { +class RequestData { public: RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs = millis()); - void setMeshInstance(EspnowMeshBackend &meshInstance); - EspnowMeshBackend &getMeshInstance(); + void setMeshInstance(const EspnowMeshBackend &meshInstance); + EspnowMeshBackend &getMeshInstance() const; + const TimeTracker &getTimeTracker() const; private: + TimeTracker _timeTracker; EspnowMeshBackend &_meshInstance; }; diff --git a/libraries/ESP8266WiFiMesh/src/ResponseData.cpp b/libraries/ESP8266WiFiMesh/src/ResponseData.cpp index b3678d246..537056ea2 100644 --- a/libraries/ESP8266WiFiMesh/src/ResponseData.cpp +++ b/libraries/ESP8266WiFiMesh/src/ResponseData.cpp @@ -25,13 +25,13 @@ #include "ResponseData.h" ResponseData::ResponseData(const String &message, const uint8_t recipientMac[6], uint64_t requestID, uint32_t creationTimeMs) : - TimeTracker(creationTimeMs), _message(message), _requestID(requestID) + _timeTracker(creationTimeMs), _message(message), _requestID(requestID) { storeRecipientMac(recipientMac); } ResponseData::ResponseData(const ResponseData &other) - : TimeTracker(other), _message(other.getMessage()), _requestID(other.getRequestID()) + : _timeTracker(other.getTimeTracker()), _message(other.getMessage()), _requestID(other.getRequestID()) { storeRecipientMac(other.getRecipientMac()); } @@ -40,7 +40,7 @@ ResponseData & ResponseData::operator=(const ResponseData &other) { if(this != &other) { - TimeTracker::operator=(other); + _timeTracker = other.getTimeTracker(); _message = other.getMessage(); _requestID = other.getRequestID(); storeRecipientMac(other.getRecipientMac()); @@ -51,21 +51,20 @@ ResponseData & ResponseData::operator=(const ResponseData &other) void ResponseData::storeRecipientMac(const uint8_t newRecipientMac[6]) { - if(newRecipientMac != nullptr) - { - if(_recipientMac == nullptr) - { - _recipientMac = _recipientMacArray; - } - - for(int i = 0; i < 6; i++) - { - _recipientMac[i] = newRecipientMac[i]; - } - } - else + if(newRecipientMac == nullptr) { _recipientMac = nullptr; + return; + } + + if(_recipientMac == nullptr) + { + _recipientMac = _recipientMacArray; + } + + for(int i = 0; i < 6; ++i) + { + _recipientMac[i] = newRecipientMac[i]; } } @@ -77,3 +76,5 @@ String ResponseData::getMessage() const { return _message; } void ResponseData::setRequestID(uint64_t requestID) { _requestID = requestID; } uint64_t ResponseData::getRequestID() const { return _requestID; } + +const TimeTracker &ResponseData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/ResponseData.h b/libraries/ESP8266WiFiMesh/src/ResponseData.h index 17b0bcc7d..982222d70 100644 --- a/libraries/ESP8266WiFiMesh/src/ResponseData.h +++ b/libraries/ESP8266WiFiMesh/src/ResponseData.h @@ -28,7 +28,7 @@ #include "TimeTracker.h" #include -class ResponseData : public TimeTracker { +class ResponseData { public: @@ -44,11 +44,15 @@ public: String getMessage() const; void setRequestID(uint64_t requestID); - uint64_t getRequestID() const; + uint64_t getRequestID() const; + + const TimeTracker &getTimeTracker() const; private: void storeRecipientMac(const uint8_t newRecipientMac[6]); + + TimeTracker _timeTracker; uint8_t _recipientMacArray[6] {0}; uint8_t *_recipientMac = nullptr; diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp index 0b1e6dfa8..34bb7bc90 100644 --- a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp @@ -32,8 +32,8 @@ namespace const IPAddress TcpIpMeshBackend::emptyIP; -bool TcpIpMeshBackend::_tcpIpTransmissionMutex = false; -bool TcpIpMeshBackend::_tcpIpConnectionQueueMutex = false; +std::shared_ptr TcpIpMeshBackend::_tcpIpTransmissionMutex = std::make_shared(false); +std::shared_ptr TcpIpMeshBackend::_tcpIpConnectionQueueMutex = std::make_shared(false); String TcpIpMeshBackend::lastSSID; bool TcpIpMeshBackend::staticIPActivated = false; @@ -51,7 +51,7 @@ std::vector TcpIpMeshBackend::_latestTransmissionOutcomes = TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) - : MeshBackendBase(requestHandler, responseHandler, networkFilter, MB_TCP_IP), _server(serverPort) + : MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::TCP_IP), _server(serverPort) { setSSID(ssidPrefix, emptyString, ssidSuffix); setMeshPassword(meshPassword); @@ -111,7 +111,7 @@ void TcpIpMeshBackend::deactivateAPHook() _server.stop(); } -bool TcpIpMeshBackend::transmissionInProgress(){return _tcpIpTransmissionMutex;} +bool TcpIpMeshBackend::transmissionInProgress(){return *_tcpIpTransmissionMutex;} void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;} String TcpIpMeshBackend::getTemporaryMessage() {return _temporaryMessage;} @@ -180,12 +180,12 @@ void TcpIpMeshBackend::setMaxAPStations(uint8_t maxAPStations) bool TcpIpMeshBackend::getMaxAPStations() {return _maxAPStations;} -void TcpIpMeshBackend::setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs) +void TcpIpMeshBackend::setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs) { _connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; } -int32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;} +uint32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;} void TcpIpMeshBackend::setStationModeTimeout(int stationModeTimeoutMs) { @@ -220,12 +220,11 @@ void TcpIpMeshBackend::fullStop(WiFiClient &currClient) */ bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait) { - uint32_t connectionStartTime = millis(); - uint32_t waitingTime = millis() - connectionStartTime; - while(currClient.connected() && !currClient.available() && waitingTime < maxWait) + ExpiringTimeTracker timeout(maxWait); + + while(currClient.connected() && !currClient.available() && !timeout) { delay(1); - waitingTime = millis() - connectionStartTime; } /* Return false if the client isn't ready to communicate */ @@ -246,7 +245,7 @@ bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_ * @return A status code based on the outcome of the exchange. * */ -transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient) +TransmissionStatusType TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient) { verboseModePrint(String(F("Transmitting"))); @@ -256,13 +255,13 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient) if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) { fullStop(currClient); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } if (!currClient.available()) { verboseModePrint(F("No response!")); - return TS_TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks. + return TransmissionStatusType::TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks. } String response = currClient.readStringUntil('\r'); @@ -278,7 +277,7 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient) * * @return A status code based on the outcome of the data transfer attempt. */ -transmission_status_t TcpIpMeshBackend::attemptDataTransfer() +TransmissionStatusType TcpIpMeshBackend::attemptDataTransfer() { // Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations. // We cannot send data to the AP in STA_AP mode though, that requires STA mode. @@ -286,7 +285,7 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransfer() WiFiMode_t storedWiFiMode = WiFi.getMode(); WiFi.mode(WIFI_STA); delay(1); - transmission_status_t transmissionOutcome = attemptDataTransferKernel(); + TransmissionStatusType transmissionOutcome = attemptDataTransferKernel(); WiFi.mode(storedWiFiMode); delay(1); @@ -298,7 +297,7 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransfer() * * @return A status code based on the outcome of the data transfer attempt. */ -transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel() +TransmissionStatusType TcpIpMeshBackend::attemptDataTransferKernel() { WiFiClient currClient; currClient.setTimeout(_stationModeTimeoutMs); @@ -308,11 +307,11 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel() { fullStop(currClient); verboseModePrint(F("Server unavailable")); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } - transmission_status_t transmissionOutcome = exchangeInfo(currClient); - if (transmissionOutcome <= 0) + TransmissionStatusType transmissionOutcome = exchangeInfo(currClient); + if (static_cast(transmissionOutcome) <= 0) { verboseModePrint(F("Transmission failed during exchangeInfo.")); return transmissionOutcome; @@ -343,7 +342,7 @@ void TcpIpMeshBackend::initiateConnectionToAP(const String &targetSSID, int targ * @return A status code based on the outcome of the connection and data transfer process. * */ -transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) +TransmissionStatusType TcpIpMeshBackend::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) { if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. { @@ -366,37 +365,36 @@ transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID, verboseModePrint(F("Connecting... "), false); initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); - int connectionStartTime = millis(); int attemptNumber = 1; + ExpiringTimeTracker connectionAttemptTimeout([this](){ return _connectionAttemptTimeoutMs; }); - int waitingTime = millis() - connectionStartTime; - while((WiFi.status() == WL_DISCONNECTED) && waitingTime <= _connectionAttemptTimeoutMs) + while((WiFi.status() == WL_DISCONNECTED) && !connectionAttemptTimeout) { - if(waitingTime > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt. + if(connectionAttemptTimeout.elapsedTime() > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt. { verboseModePrint(F("... "), false); WiFi.disconnect(); yield(); initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); - attemptNumber++; + ++attemptNumber; } + delay(1); - waitingTime = millis() - connectionStartTime; } - verboseModePrint(String(waitingTime)); + verboseModePrint(String(connectionAttemptTimeout.elapsedTime())); /* If the connection timed out */ if (WiFi.status() != WL_CONNECTED) { verboseModePrint(F("Timeout")); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } return attemptDataTransfer(); } -transmission_status_t TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo) +TransmissionStatusType TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo) { WiFi.disconnect(); yield(); @@ -452,7 +450,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo if(WiFi.status() == WL_CONNECTED) { - transmission_status_t transmissionResult = attemptDataTransfer(); + TransmissionStatusType transmissionResult = attemptDataTransfer(); latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult)); getTransmissionOutcomesUpdateHook()(*this); @@ -474,7 +472,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo { for(const TcpIpNetworkInfo ¤tNetwork : constConnectionQueue()) { - transmission_status_t transmissionResult = initiateTransmission(currentNetwork); + TransmissionStatusType transmissionResult = initiateTransmission(currentNetwork); latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); @@ -492,16 +490,16 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo attemptTransmission(message, scan, scanAllWiFiChannels, true, false); } -transmission_status_t TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect, bool initialDisconnect) +TransmissionStatusType TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect, bool initialDisconnect) { MutexTracker mutexTracker(_tcpIpTransmissionMutex); if(!mutexTracker.mutexCaptured()) { assert(false && String(F("ERROR! TCP/IP transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); - return TS_CONNECTION_FAILED; + return TransmissionStatusType::CONNECTION_FAILED; } - transmission_status_t transmissionResult = TS_CONNECTION_FAILED; + TransmissionStatusType transmissionResult = TransmissionStatusType::CONNECTION_FAILED; setTemporaryMessage(message); if(initialDisconnect) diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h index 632bd075d..4bc6dfed5 100644 --- a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h @@ -43,14 +43,14 @@ public: * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which * is the request string received from another node and returns the string to send back. * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which - * is the response string received from another node. Returns a transmission status code as a transmission_status_t. + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. * @param networkFilter The callback handler for deciding which WiFi networks to connect to. * @param meshPassword The WiFi password for the mesh network. * @param ssidPrefix The prefix (first part) of the node SSID. * @param ssidSuffix The suffix (last part) of the node SSID. * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is separate for each TcpIpMeshBackend instance. * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly @@ -91,7 +91,7 @@ public: static std::vector & latestTransmissionOutcomes(); /** - * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. * The result is unique for each mesh backend. */ static bool latestTransmissionSuccessful(); @@ -124,7 +124,7 @@ public: * * Note that if wifiChannel and BSSID are missing from recipientInfo, connection time will be longer. */ - transmission_status_t attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect = true, bool initialDisconnect = false); + TransmissionStatusType attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect = true, bool initialDisconnect = false); /** * If any clients are connected, accept their requests and call the requestHandler function for each one. @@ -183,8 +183,8 @@ public: * * @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds. */ - void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs); - int32_t getConnectionAttemptTimeout(); + void setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs); + uint32_t getConnectionAttemptTimeout(); /** * Set the timeout to use for transmissions when this TcpIpMeshBackend instance acts as a station (i.e. when connected to another AP). @@ -228,12 +228,12 @@ protected: /** * Will be true if a transmission initiated by a public method is in progress. */ - static bool _tcpIpTransmissionMutex; + static std::shared_ptr _tcpIpTransmissionMutex; /** * Will be true when the connectionQueue should not be modified. */ - static bool _tcpIpConnectionQueueMutex; + static std::shared_ptr _tcpIpConnectionQueueMutex; /** * Check if there is an ongoing TCP/IP transmission in the library. Used to avoid interrupting transmissions. @@ -257,7 +257,7 @@ private: uint16_t _serverPort; WiFiServer _server; uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections - int32_t _connectionAttemptTimeoutMs = 10000; + uint32_t _connectionAttemptTimeoutMs = 10000; int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. uint32_t _apModeTimeoutMs = 4500; @@ -271,12 +271,12 @@ private: void fullStop(WiFiClient &currClient); void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); - transmission_status_t connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); - transmission_status_t exchangeInfo(WiFiClient &currClient); + TransmissionStatusType connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); + TransmissionStatusType exchangeInfo(WiFiClient &currClient); bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); - transmission_status_t attemptDataTransfer(); - transmission_status_t attemptDataTransferKernel(); - transmission_status_t initiateTransmission(const TcpIpNetworkInfo &recipientInfo); + TransmissionStatusType attemptDataTransfer(); + TransmissionStatusType attemptDataTransferKernel(); + TransmissionStatusType initiateTransmission(const TcpIpNetworkInfo &recipientInfo); void enterPostTransmissionState(bool concludingDisconnect); }; diff --git a/libraries/ESP8266WiFiMesh/src/TimeTracker.h b/libraries/ESP8266WiFiMesh/src/TimeTracker.h index 76e5eafe4..77068228e 100644 --- a/libraries/ESP8266WiFiMesh/src/TimeTracker.h +++ b/libraries/ESP8266WiFiMesh/src/TimeTracker.h @@ -27,6 +27,7 @@ #include +// Minimal time tracking class. Used instead of other classes like ExpiringTimeTracker when small memory footprint is important and other functionality not required. class TimeTracker { public: diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp index 24c741bca..5dec1978a 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp +++ b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp @@ -24,13 +24,13 @@ #include "TransmissionOutcome.h" -TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t transmissionStatus) +TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, TransmissionStatusType transmissionStatus) : NetworkInfoBase(origin), _transmissionStatus(transmissionStatus) { } -TransmissionOutcome::TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, transmission_status_t transmissionStatus) +TransmissionOutcome::TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, TransmissionStatusType transmissionStatus) : NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden), _transmissionStatus(transmissionStatus) { } -void TransmissionOutcome::setTransmissionStatus(transmission_status_t transmissionStatus) { _transmissionStatus = transmissionStatus; } -transmission_status_t TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; } +void TransmissionOutcome::setTransmissionStatus(TransmissionStatusType transmissionStatus) { _transmissionStatus = transmissionStatus; } +TransmissionStatusType TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; } diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h index 026556d7b..91d703a8b 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h +++ b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h @@ -28,27 +28,27 @@ #include #include "NetworkInfoBase.h" -typedef enum +enum class TransmissionStatusType { - TS_CONNECTION_FAILED = -1, - TS_TRANSMISSION_FAILED = 0, - TS_TRANSMISSION_COMPLETE = 1 -} transmission_status_t; + CONNECTION_FAILED = -1, + TRANSMISSION_FAILED = 0, + TRANSMISSION_COMPLETE = 1 +}; class TransmissionOutcome : public NetworkInfoBase { public: - TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t transmissionStatus); + TransmissionOutcome(const NetworkInfoBase &origin, TransmissionStatusType transmissionStatus); - TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, transmission_status_t transmissionStatus); + TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, TransmissionStatusType transmissionStatus); - void setTransmissionStatus(transmission_status_t transmissionStatus); - transmission_status_t transmissionStatus() const; + void setTransmissionStatus(TransmissionStatusType transmissionStatus); + TransmissionStatusType transmissionStatus() const; private: - transmission_status_t _transmissionStatus; + TransmissionStatusType _transmissionStatus; }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h index 86c9b0b08..709e258f9 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h +++ b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h @@ -54,6 +54,13 @@ #include "NetworkInfo.h" #include "TransmissionOutcome.h" +typedef enum +{ + TS_CONNECTION_FAILED = -1, + TS_TRANSMISSION_FAILED = 0, + TS_TRANSMISSION_COMPLETE = 1 +} transmission_status_t; + class TransmissionResult : public NetworkInfo { public: @@ -63,11 +70,11 @@ public: /** * @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. */ - TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true); + TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true) __attribute__((deprecated)); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus) __attribute__((deprecated)); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus) __attribute__((deprecated)); TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus); }; diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp index 1faa8d562..b4073bfac 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp @@ -27,15 +27,15 @@ namespace { - constexpr char chars[36] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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'}; - constexpr uint8_t charValues[75] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 10 + constexpr char chars[36] PROGMEM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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'}; + constexpr uint8_t charValues[75] PROGMEM {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; // Lower case letters } namespace MeshTypeConversionFunctions { - String uint64ToString(uint64_t number, byte base) + String uint64ToString(uint64_t number, const byte base) { assert(2 <= base && base <= 36); @@ -44,14 +44,14 @@ namespace MeshTypeConversionFunctions if(base == 16) { do { - result += chars[ number % base ]; + result += (char)pgm_read_byte(chars + number % base); number >>= 4; // We could write number /= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from. } while ( number ); } else { do { - result += chars[ number % base ]; + result += (char)pgm_read_byte(chars + number % base); number /= base; } while ( number ); } @@ -61,7 +61,7 @@ namespace MeshTypeConversionFunctions return result; } - uint64_t stringToUint64(const String &string, byte base) + uint64_t stringToUint64(const String &string, const byte base) { assert(2 <= base && base <= 36); @@ -72,7 +72,7 @@ namespace MeshTypeConversionFunctions for(uint32_t i = 0; i < string.length(); ++i) { result <<= 4; // We could write result *= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from. - result += charValues[string.charAt(i) - '0']; + result += pgm_read_byte(charValues + string.charAt(i) - '0'); } } else @@ -80,14 +80,14 @@ namespace MeshTypeConversionFunctions for(uint32_t i = 0; i < string.length(); ++i) { result *= base; - result += charValues[string.charAt(i) - '0']; + result += pgm_read_byte(charValues + string.charAt(i) - '0'); } } return result; } - String uint8ArrayToHexString(const uint8_t *uint8Array, uint32_t arrayLength) + String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength) { String hexString; if(!hexString.reserve(2*arrayLength)) // Each uint8_t will become two characters (00 to FF) @@ -95,26 +95,26 @@ namespace MeshTypeConversionFunctions for(uint32_t i = 0; i < arrayLength; ++i) { - hexString += chars[ uint8Array[i] >> 4 ]; - hexString += chars[ uint8Array[i] % 16 ]; + hexString += (char)pgm_read_byte(chars + (uint8Array[i] >> 4)); + hexString += (char)pgm_read_byte(chars + uint8Array[i] % 16 ); } return hexString; } - uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, uint32_t arrayLength) + uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength) { assert(hexString.length() >= arrayLength*2); // Each array element can hold two hexString characters for(uint32_t i = 0; i < arrayLength; ++i) { - uint8Array[i] = (charValues[hexString.charAt(i*2) - '0'] << 4) + charValues[hexString.charAt(i*2 + 1) - '0']; + uint8Array[i] = (pgm_read_byte(charValues + hexString.charAt(i*2) - '0') << 4) + pgm_read_byte(charValues + hexString.charAt(i*2 + 1) - '0'); } return uint8Array; } - String uint8ArrayToMultiString(uint8_t *uint8Array, uint32_t arrayLength) + String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength) { String multiString; if(!multiString.reserve(arrayLength)) @@ -137,7 +137,7 @@ namespace MeshTypeConversionFunctions return multiString; } - String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, uint32_t arrayLength) + String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength) { String multiString; if(!multiString.reserve(arrayLength)) @@ -174,7 +174,7 @@ namespace MeshTypeConversionFunctions return result; } - uint8_t *uint64ToMac(uint64_t macValue, uint8_t *macArray) + uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray) { assert(macValue <= 0xFFFFFFFFFFFF); // Overflow will occur if value can't fit within 6 bytes @@ -188,7 +188,7 @@ namespace MeshTypeConversionFunctions return macArray; } - uint8_t *uint64ToUint8Array(uint64_t value, uint8_t *resultArray) + uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray) { resultArray[7] = value; resultArray[6] = value >> 8; @@ -214,27 +214,25 @@ namespace MeshTypeConversionFunctions * Helper function for meshBackendCast. */ template - T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, mesh_backend_t resultClassType) + T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, MeshBackendType resultClassType) { if(meshBackendBaseInstance && meshBackendBaseInstance->getClassType() == resultClassType) { return static_cast(meshBackendBaseInstance); } - else - { - return nullptr; - } + + return nullptr; } template <> EspnowMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance) { - return attemptPointerCast(meshBackendBaseInstance, MB_ESP_NOW); + return attemptPointerCast(meshBackendBaseInstance, MeshBackendType::ESP_NOW); } template <> TcpIpMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance) { - return attemptPointerCast(meshBackendBaseInstance, MB_TCP_IP); + return attemptPointerCast(meshBackendBaseInstance, MeshBackendType::TCP_IP); } } diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h index 07591a816..4c678a4ec 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h @@ -42,7 +42,7 @@ namespace MeshTypeConversionFunctions * @param base The radix to convert "number" into. Must be between 2 and 36. * @return A string of "number" encoded in radix "base". */ - String uint64ToString(uint64_t number, byte base = 16); + String uint64ToString(uint64_t number, const byte base = 16); /** * Note that using base 10 instead of 16 increases conversion time by roughly a factor of 2, due to unfavourable 64-bit arithmetic. @@ -52,7 +52,7 @@ namespace MeshTypeConversionFunctions * @param base The radix of "string". Must be between 2 and 36. * @return A uint64_t of the string, using radix "base" during decoding. */ - uint64_t stringToUint64(const String &string, byte base = 16); + uint64_t stringToUint64(const String &string, const byte base = 16); /** * Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array. @@ -62,7 +62,7 @@ namespace MeshTypeConversionFunctions * @param arrayLength The size of uint8Array, in bytes. * @return Normally a String containing the HEX representation of the uint8Array. An empty String if the memory allocation for the String failed. */ - String uint8ArrayToHexString(const uint8_t *uint8Array, uint32_t arrayLength); + String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength); /** * Convert the contents of a String in HEX format to a uint8_t array. Index 0 of the array will represent the start of the String. @@ -73,7 +73,7 @@ namespace MeshTypeConversionFunctions * @param arrayLength The number of bytes to fill in uint8Array. * @return A pointer to the uint8Array. */ - uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, uint32_t arrayLength); + uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength); /** * Stores the exact values of uint8Array in a String, even null values. @@ -86,7 +86,7 @@ namespace MeshTypeConversionFunctions * @param arrayLength The size of uint8Array, in bytes. * @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed. */ - String uint8ArrayToMultiString(uint8_t *uint8Array, uint32_t arrayLength); + String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength); /** * Stores the exact values of uint8Array in a String, even null values. @@ -99,7 +99,7 @@ namespace MeshTypeConversionFunctions * @param arrayLength The size of uint8Array, in bytes. * @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed. */ - String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, uint32_t arrayLength); + String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength); /** * Takes a uint8_t array and converts the first 6 bytes to a hexadecimal string. @@ -133,7 +133,7 @@ namespace MeshTypeConversionFunctions * @param macArray A uint8_t array that will hold the mac address once the function returns. Should have a size of at least 6 bytes. * @return The macArray. */ - uint8_t *uint64ToMac(uint64_t macValue, uint8_t *macArray); + uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray); /** * Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB. @@ -142,7 +142,7 @@ namespace MeshTypeConversionFunctions * @param resultArray A uint8_t array that will hold the result once the function returns. Should have a size of at least 8 bytes. * @return The resultArray. */ - uint8_t *uint64ToUint8Array(uint64_t value, uint8_t *resultArray); + uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray); /** * Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB. diff --git a/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp index 08dfb5111..209bdf80b 100644 --- a/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp +++ b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp @@ -30,7 +30,7 @@ namespace MeshUtilityFunctions { bool macEqual(const uint8_t *macOne, const uint8_t *macTwo) { - for(int i = 0; i <= 5; i++) + for(int i = 0; i <= 5; ++i) { if(macOne[i] != macTwo[i]) {