1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

- 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.
This commit is contained in:
Anders 2020-03-05 15:30:20 +01:00
parent a49f047096
commit 16801f3dac
38 changed files with 924 additions and 679 deletions

View File

@ -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 <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <EspnowMeshBackend.h> #include <EspnowMeshBackend.h>
@ -40,7 +40,7 @@ unsigned int responseNumber = 0;
const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII
String manageRequest(const String &request, MeshBackendBase &meshInstance); 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); void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &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. @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) { 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) // 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<EspnowMeshBackend *>(&meshInstance)) { if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission"; String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission");
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): "); Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): "));
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) { } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
(void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. (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 { } else {
Serial.print("UNKNOWN!: "); Serial.print(F("UNKNOWN!: "));
} }
/* Print out received message */ /* 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. // 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. // 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. // 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) { if (request.charAt(0) == 0) {
Serial.println(request); // substring will not work for multiStrings. 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 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. @param meshInstance The MeshBackendBase instance that called the function.
@return The status code resulting from the response, as an int @return The status code resulting from the response, as an int
*/ */
transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance) { TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) {
transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; 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) // 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<EspnowMeshBackend *>(&meshInstance)) { if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission"; String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission");
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): "); Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): "));
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) { } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&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. // 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. // 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.print(F("Request sent: "));
Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100)); Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100));
} else { } else {
Serial.print("UNKNOWN!: "); Serial.print(F("UNKNOWN!: "));
} }
/* Print out received message */ /* Print out received message */
@ -146,7 +141,7 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) { } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
tcpIpInstance->connectionQueue().push_back(networkIndex); tcpIpInstance->connectionQueue().push_back(networkIndex);
} else { } 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); 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 return false; // Broadcast is for another mesh network
} else { } else {
// Remove metadata from message and mark as accepted broadcast. // 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. // 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. // 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(). // 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.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook);
espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook); espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook);
@ -290,7 +285,7 @@ void setup() {
// Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received. // 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. // 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. // 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); //espnowNode.setUseEncryptedMessages(true);
} }
@ -306,11 +301,11 @@ void loop() {
EspnowMeshBackend::performEspnowMaintenance(); EspnowMeshBackend::performEspnowMaintenance();
if (millis() - timeOfLastScan > 10000) { // Give other nodes some time to connect between data transfers. 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(); uint32_t startTime = millis();
espnowNode.attemptTransmission(espnowNode.getMessage()); 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(); timeOfLastScan = millis();
@ -328,19 +323,19 @@ void loop() {
Serial.println(F("No mesh AP found.")); Serial.println(F("No mesh AP found."));
} else { } else {
for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) { 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()); 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()); 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. // No need to do anything, transmission was successful.
} else { } 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); assert(F("Invalid transmission status returned from responseHandler!") && false);
} }
} }
Serial.println("\nPerforming ESP-NOW broadcast."); Serial.println(F("\nPerforming ESP-NOW broadcast."));
startTime = millis(); startTime = millis();
@ -348,9 +343,9 @@ void loop() {
// Note that data that comes before broadcastMetadataDelimiter should not contain any broadcastMetadataDelimiter characters, // 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. // otherwise the broadcastFilter function used in this example file will not work.
String broadcastMetadata = espnowNode.getMeshName() + String(broadcastMetadataDelimiter); 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); 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). 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. // 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. // 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'}; 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(".")); String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.');
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response. 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}; 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. // 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. // 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); 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. // Making a transmission now will cause messages to targetBSSID to be encrypted.
String espnowMessage = "This message is encrypted only when received by node " + peerMac; String espnowMessage = String(F("This message is encrypted only when received by node ")) + peerMac;
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response. espnowDelay(100); // Wait for response.
@ -389,48 +384,48 @@ void loop() {
espnowNode.removeEncryptedConnection(targetBSSID); 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. // 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; espnowMessage = String(F("This message is no longer encrypted when received by node ")) + peerMac;
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response. 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! // Let's re-add our stored connection so we can communicate properly with targetBSSID again!
espnowNode.addEncryptedConnection(serializedEncryptedConnection); espnowNode.addEncryptedConnection(serializedEncryptedConnection);
espnowMessage = "This message is once again encrypted when received by node " + peerMac; espnowMessage = String(F("This message is once again encrypted when received by node ")) + peerMac;
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response. espnowDelay(100); // Wait for response.
Serial.println(); Serial.println();
// If we want to remove the encrypted connection on both nodes, we can do it like this. // 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); EncryptedConnectionRemovalOutcome removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID);
if (removalOutcome == ECRO_REMOVAL_SUCCEEDED) { if (removalOutcome == EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED) {
Serial.println(peerMac + " is no longer encrypted!"); 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."; 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("Transmitting: " + espnowMessage); Serial.println(String(F("Transmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, EspnowNetworkInfo(targetBSSID)); espnowNode.attemptTransmission(espnowMessage, EspnowNetworkInfo(targetBSSID));
espnowDelay(100); // Wait for response. espnowDelay(100); // Wait for response.
Serial.println(); Serial.println();
// Of course, we can also just create a temporary encrypted connection that will remove itself once its duration has passed. // 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); espnowDelay(42);
uint32_t remainingDuration = 0; uint32_t remainingDuration = 0;
EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowMessage = "Messages this node sends to " + peerMac + " will be encrypted for " + String(remainingDuration) + " ms more."; espnowMessage = String(F("Messages this node sends to ")) + peerMac + F(" will be encrypted for ") + String(remainingDuration) + F(" ms more.");
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowDelay(remainingDuration + 100); espnowDelay(remainingDuration + 100);
espnowMessage = "Due to encrypted connection expiration, this message is no longer encrypted when received by node " + peerMac; espnowMessage = String(F("Due to encrypted connection expiration, this message is no longer encrypted when received by node ")) + peerMac;
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response. 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. // 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. // 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. // 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."; espnowMessage = F("This message is always encrypted, regardless of receiver.");
Serial.println("\nTransmitting: " + espnowMessage); Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptAutoEncryptingTransmission(espnowMessage); espnowNode.attemptAutoEncryptingTransmission(espnowMessage);
espnowDelay(100); // Wait for response. espnowDelay(100); // Wait for response.
} else { } else {
Serial.println("Ooops! Encrypted connection removal failed. Status: " + String(removalOutcome)); Serial.println(String(F("Ooops! Encrypted connection removal failed. Status: ")) + String(static_cast<int>(removalOutcome)));
} }
// Finally, should you ever want to stop other parties from sending unencrypted messages to the node // Finally, should you ever want to stop other parties from sending unencrypted messages to the node
// setAcceptsUnencryptedRequests(false); // setAcceptsUnencryptedRequests(false);
// can be used for this. It applies to both encrypted connection requests and regular transmissions. // 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. // 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.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ")
+ espnowNode.getMeshName() + espnowNode.getNodeID() + String(F("."))); + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'));
} }
Serial.println(); Serial.println();

View File

@ -5,7 +5,7 @@
That way you will get instant confirmation of the mesh communication without checking the Serial Monitor. 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 <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <TypeConversionFunctions.h> #include <TypeConversionFunctions.h>
@ -175,7 +175,7 @@ void loop() {
floodingMeshDelay(1); 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): // 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)); // Encrypted (slow): floodingMesh.getEspnowMeshBackend().attemptAutoEncryptingTransmission(message, EspnowNetworkInfo(recipientMac));
if (theOne) { if (theOne) {

View File

@ -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 <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <TcpIpMeshBackend.h> #include <TcpIpMeshBackend.h>
@ -25,7 +25,7 @@ unsigned int requestNumber = 0;
unsigned int responseNumber = 0; unsigned int responseNumber = 0;
String manageRequest(const String &request, MeshBackendBase &meshInstance); 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); void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
/* Create the mesh node object */ /* 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. @param meshInstance The MeshBackendBase instance that called the function.
@return The status code resulting from the response, as an int @return The status code resulting from the response, as an int
*/ */
transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance) { TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) {
transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; 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) // 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<EspnowMeshBackend *>(&meshInstance)) { if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
@ -142,7 +142,7 @@ bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) {
// The default hook only returns true and does nothing else. // The default hook only returns true and does nothing else.
if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) { if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&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. // 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.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ")
+ meshInstance.getMeshName() + meshInstance.getNodeID() + String('.')); + meshInstance.getMeshName() + meshInstance.getNodeID() + String('.'));
@ -209,11 +209,11 @@ void loop() {
Serial.println(F("No mesh AP found.")); Serial.println(F("No mesh AP found."));
} else { } else {
for (TransmissionOutcome &transmissionOutcome : tcpIpNode.latestTransmissionOutcomes()) { 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()); 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()); 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. // No need to do anything, transmission was successful.
} else { } else {
Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!')); Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!'));

View File

@ -12,7 +12,7 @@ ESP8266WiFiMesh KEYWORD3
# Datatypes (KEYWORD1) # Datatypes (KEYWORD1)
####################################### #######################################
transmission_status_t KEYWORD1 TransmissionStatusType KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)

View File

@ -35,8 +35,6 @@ namespace
size_t _ctMinDataLength = 0; size_t _ctMinDataLength = 0;
size_t _ctMaxDataLength = 1024; size_t _ctMaxDataLength = 1024;
bool _warningsEnabled = true;
br_hkdf_context _storedHkdfContext; br_hkdf_context _storedHkdfContext;
bool _hkdfContextStored = false; bool _hkdfContextStored = false;
@ -186,26 +184,44 @@ namespace
createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength); createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
return TypeCast::uint8ArrayToHexString(hmac, hmacLength); return TypeCast::uint8ArrayToHexString(hmac, hmacLength);
} }
// Helper function to avoid deprecated warnings.
void *md5HashHelper(const void *data, const size_t dataLength, void *resultArray)
{
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 namespace CryptoInterface
{ {
void setCtMinDataLength(const size_t ctMinDataLength) void setCtMinDataLength(const size_t ctMinDataLength)
{ {
assert(ctMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF); assert(getCtMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF);
_ctMinDataLength = ctMinDataLength; _ctMinDataLength = ctMinDataLength;
} }
size_t ctMinDataLength() {return _ctMinDataLength;} size_t getCtMinDataLength() {return _ctMinDataLength;}
void setCtMaxDataLength(const size_t ctMaxDataLength) void setCtMaxDataLength(const size_t ctMaxDataLength)
{ {
assert(ctMaxDataLength - ctMinDataLength() <= CT_MAX_DIFF); assert(ctMaxDataLength - getCtMinDataLength() <= CT_MAX_DIFF);
_ctMaxDataLength = ctMaxDataLength; _ctMaxDataLength = ctMaxDataLength;
} }
size_t ctMaxDataLength() {return _ctMaxDataLength;} size_t getCtMaxDataLength() {return _ctMaxDataLength;}
void setWarningsEnabled(bool warningsEnabled) { _warningsEnabled = warningsEnabled; }
bool warningsEnabled() { return _warningsEnabled; }
void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; } void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; }
nonceGeneratorType getNonceGenerator() { return _nonceGenerator; } nonceGeneratorType getNonceGenerator() { return _nonceGenerator; }
@ -216,22 +232,13 @@ namespace CryptoInterface
// resultArray must have size MD5_NATURAL_LENGTH or greater // resultArray must have size MD5_NATURAL_LENGTH or greater
void *md5Hash(const void *data, const size_t dataLength, void *resultArray) void *md5Hash(const void *data, const size_t dataLength, void *resultArray)
{ {
if(warningsEnabled()) return md5HashHelper(data, dataLength, resultArray);
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;
} }
String md5Hash(const String &message) String md5Hash(const String &message)
{ {
uint8_t hash[MD5_NATURAL_LENGTH]; 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); return TypeCast::uint8ArrayToHexString(hash, MD5_NATURAL_LENGTH);
} }
@ -261,22 +268,13 @@ namespace CryptoInterface
// resultArray must have size SHA1_NATURAL_LENGTH or greater // resultArray must have size SHA1_NATURAL_LENGTH or greater
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray) void *sha1Hash(const void *data, const size_t dataLength, void *resultArray)
{ {
if(warningsEnabled()) return sha1HashHelper(data, dataLength, resultArray);
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;
} }
String sha1Hash(const String &message) String sha1Hash(const String &message)
{ {
uint8_t hash[SHA1_NATURAL_LENGTH]; 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); return TypeCast::uint8ArrayToHexString(hash, SHA1_NATURAL_LENGTH);
} }

View File

@ -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, * 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." * 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, * For messages much smaller than getCtMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
* determined by the size of (ctMaxDataLength() - ctMinDataLength()). * determined by the size of (getCtMaxDataLength() - getCtMinDataLength()).
* Constant-time processing also sets limits on the data length. * 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. * 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. * 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. * 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); void setCtMinDataLength(const size_t ctMinDataLength);
/** /**
* 0 by default. * 0 by default.
*/ */
size_t ctMinDataLength(); size_t getCtMinDataLength();
/** /**
* This function allows for fine-tuning of the specifications for the constant time calculations. * 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. * 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. * 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); void setCtMaxDataLength(const size_t ctMaxDataLength);
/** /**
* 1024 by default. * 1024 by default.
*/ */
size_t ctMaxDataLength(); size_t getCtMaxDataLength();
/**
* 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();
/** /**
* Set the nonce generator used by the CryptoInterface functions. * Set the nonce generator used by the CryptoInterface functions.
@ -119,6 +111,9 @@ namespace CryptoInterface
// #################### MD5 #################### // #################### 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. * 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. * Uses the BearSSL cryptographic library.
* *
@ -128,9 +123,12 @@ namespace CryptoInterface
* *
* @return A pointer to resultArray. * @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. * 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. * Uses the BearSSL cryptographic library.
* *
@ -138,7 +136,7 @@ namespace CryptoInterface
* *
* @return A String with the generated hash in HEX format. * @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. * 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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -193,7 +191,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @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 #################### // #################### 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. * 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. * Uses the BearSSL cryptographic library.
* *
@ -215,9 +216,12 @@ namespace CryptoInterface
* *
* @return A pointer to resultArray. * @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. * 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. * Uses the BearSSL cryptographic library.
* *
@ -225,7 +229,7 @@ namespace CryptoInterface
* *
* @return A String with the generated hash in HEX format. * @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. * 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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -280,7 +284,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -367,7 +371,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -454,7 +458,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -541,7 +545,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @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. * Uses the BearSSL cryptographic library.
* *
* @param data The data array from which to create the HMAC. * @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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC. * @param resultArray The array wherein to store the resulting HMAC.
@ -628,7 +632,7 @@ namespace CryptoInterface
* Constant-time version. * Constant-time version.
* Uses the BearSSL cryptographic library. * 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 hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes. * @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. * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA512_NATURAL_LENGTH.

View File

@ -54,7 +54,7 @@ namespace TypeCast = MeshTypeConversionFunctions;
const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress(); const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress();
String ESP8266WiFiMesh::lastSSID = ""; String ESP8266WiFiMesh::lastSSID;
bool ESP8266WiFiMesh::staticIPActivated = false; 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. // 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) void ESP8266WiFiMesh::updateNetworkNames(const String &newMeshName, const String &newNodeID)
{ {
if(newMeshName != "") if(!newMeshName.isEmpty())
_meshName = newMeshName; _meshName = newMeshName;
if(newNodeID != "") if(!newNodeID.isEmpty())
_nodeID = newNodeID; _nodeID = newNodeID;
String newSSID = _meshName + _nodeID; 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) 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 #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. // 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(); WiFi.disconnect();
yield(); yield();
String currentSSID = ""; String currentSSID;
int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT; int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT;
uint8_t *currentBSSID = NULL; 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 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; currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel; currentWiFiChannel = currentNetwork.wifiChannel;

View File

@ -47,8 +47,9 @@
#include <WiFiServer.h> #include <WiFiServer.h>
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "NetworkInfo.h"
#include "TransmissionResult.h" #include "TransmissionResult.h"
#include "NetworkInfo.h"
const String WIFI_MESH_EMPTY_STRING = ""; const String WIFI_MESH_EMPTY_STRING = "";
@ -175,7 +176,7 @@ public:
*/ */
ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter,
const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, 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. * A vector that contains the NetworkInfo for each WiFi network to connect to.

View File

@ -102,10 +102,8 @@ bool EncryptedConnectionData::connectedTo(const uint8_t *peerMac) const
{ {
return true; return true;
} }
else
{ return false;
return false;
}
} }
void EncryptedConnectionData::setHashKey(const uint8_t hashKey[espnowHashKeyLength]) void EncryptedConnectionData::setHashKey(const uint8_t hashKey[espnowHashKeyLength])

File diff suppressed because it is too large Load Diff

View File

@ -90,32 +90,34 @@
#include "EspnowNetworkInfo.h" #include "EspnowNetworkInfo.h"
#include "CryptoInterface.h" #include "CryptoInterface.h"
typedef enum namespace Espnow
{ {
ECT_NO_CONNECTION = 0, enum class ConnectionType
ECT_TEMPORARY_CONNECTION = 1, {
ECT_PERMANENT_CONNECTION = 2 NO_CONNECTION = 0,
} espnow_connection_type_t; TEMPORARY_CONNECTION = 1,
PERMANENT_CONNECTION = 2
};
// A value greater than 0 means that an encrypted connection has been established. // A value greater than 0 means that an encrypted connection has been established.
typedef enum enum class EncryptedConnectionStatus
{ {
ECS_MAX_CONNECTIONS_REACHED_SELF = -3, MAX_CONNECTIONS_REACHED_SELF = -3,
ECS_REQUEST_TRANSMISSION_FAILED = -2, REQUEST_TRANSMISSION_FAILED = -2,
ECS_MAX_CONNECTIONS_REACHED_PEER = -1, MAX_CONNECTIONS_REACHED_PEER = -1,
ECS_API_CALL_FAILED = 0, API_CALL_FAILED = 0,
ECS_CONNECTION_ESTABLISHED = 1, CONNECTION_ESTABLISHED = 1,
ECS_SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6. SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6.
} encrypted_connection_status_t; };
typedef enum enum class EncryptedConnectionRemovalOutcome
{ {
ECRO_REMOVAL_REQUEST_FAILED = -1, REMOVAL_REQUEST_FAILED = -1,
ECRO_REMOVAL_FAILED = 0, REMOVAL_FAILED = 0,
ECRO_REMOVAL_SUCCEEDED = 1, REMOVAL_SUCCEEDED = 1,
ECRO_REMOVAL_SCHEDULED = 2 REMOVAL_SCHEDULED = 2
} encrypted_connection_removal_outcome_t; };
}
/** /**
* An alternative to standard delay(). Will continuously call performEspnowMaintenance() during the waiting time, so that the ESP-NOW node remains responsive. * 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; class RequestData;
using namespace Espnow; // TODO: Remove
class EspnowMeshBackend : public MeshBackendBase { class EspnowMeshBackend : public MeshBackendBase {
protected: protected:
typedef std::function<bool(String &, EspnowMeshBackend &)> broadcastFilterType; using broadcastFilterType = std::function<bool(String &, EspnowMeshBackend &)>;
typedef std::function<bool(const String &, const uint8_t *, uint32_t, EspnowMeshBackend &)> responseTransmittedHookType; using responseTransmittedHookType = std::function<bool(const String &, const uint8_t *, uint32_t, EspnowMeshBackend &)>;
public: public:
@ -145,7 +149,7 @@ public:
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which * @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. * 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 * @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 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 broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept.
* @param meshPassword The WiFi password for the mesh network. * @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 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 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. * @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. * 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 * 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 * 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 * @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. * 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 * @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 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 broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept.
* @param meshPassword The WiFi password for the mesh network. * @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 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 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. * @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. * 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 * 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 * 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<TransmissionOutcome> & latestTransmissionOutcomes(); static std::vector<TransmissionOutcome> & 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. * The result is unique for each mesh backend.
*/ */
static bool latestTransmissionSuccessful(); static bool latestTransmissionSuccessful();
@ -285,7 +289,7 @@ public:
* *
* @param recipientInfo The recipient information. * @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, * 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). * Transmit message to a single recipient without changing the local transmission state (apart from encrypted connections).
* Will not change connectionQueue, latestTransmissionOutcomes or stored message. * 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. * 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. // 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. // 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. // 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. // 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. // @param ignoreDuration Ignores any stored duration serializedConnectionState, guaranteeing that the created connection will be permanent. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
encrypted_connection_status_t addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration = false); 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). // 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. // 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. // 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. // 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. // 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. // Uses duration argument instead of any stored duration in serializedConnectionState. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
encrypted_connection_status_t addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration); 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. // 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. // 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. // 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. // 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 // 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. // depending on the time it takes to verify the connection to the node.
encrypted_connection_status_t requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs); EncryptedConnectionStatus requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs);
static encrypted_connection_removal_outcome_t removeEncryptedConnection(uint8_t *peerMac); static EncryptedConnectionRemovalOutcome removeEncryptedConnection(uint8_t *peerMac);
encrypted_connection_removal_outcome_t requestEncryptedConnectionRemoval(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. * 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). * @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. * 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. * @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. * 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. * 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 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. * @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. * 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. * @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. * 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. * @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<EspnowNetworkInfo> _connectionQueue; static std::vector<EspnowNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes; static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
typedef std::vector<EncryptedConnectionLog>::iterator connectionLogIterator; using connectionLogIterator = std::vector<EncryptedConnectionLog>::iterator;
static connectionLogIterator connectionLogEndIterator(); static connectionLogIterator connectionLogEndIterator();
static const uint8_t broadcastMac[6]; static const uint8_t broadcastMac[6];
@ -866,7 +870,7 @@ protected:
bool activateEspnow(); 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. * 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. // 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. // 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. // @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<EncryptedConnectionLog>::iterator *resultingIterator = nullptr); static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator = nullptr);
static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector<EncryptedConnectionLog>::iterator *resultingIterator); static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector<EncryptedConnectionLog>::iterator *resultingIterator);
/** /**
* Set the MAC address considered to be the sender of the most recently received ESP-NOW request, response or broadcast. * 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. * Will be true if a transmission initiated by a public method is in progress.
*/ */
static bool _espnowTransmissionMutex; static std::shared_ptr<bool> _espnowTransmissionMutex;
/** /**
* Will be true when the connectionQueue should not be modified. * Will be true when the connectionQueue should not be modified.
*/ */
static bool _espnowConnectionQueueMutex; static std::shared_ptr<bool> _espnowConnectionQueueMutex;
/** /**
* Will be true when no responsesToSend element should be removed. * Will be true when no responsesToSend element should be removed.
*/ */
static bool _responsesToSendMutex; static std::shared_ptr<bool> _responsesToSendMutex;
/** /**
* Check if there is an ongoing ESP-NOW transmission in the library. Used to avoid interrupting transmissions. * 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(); static bool transmissionInProgress();
enum class macAndType_td : uint64_t {}; enum class macAndType_td : uint64_t {};
typedef uint64_t messageID_td; using messageID_td = uint64_t;
typedef uint64_t peerMac_td; using peerMac_td = uint64_t;
static macAndType_td createMacAndTypeValue(uint64_t uint64Mac, char messageType); static macAndType_td createMacAndTypeValue(uint64_t uint64Mac, char messageType);
static uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue); static uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue);
@ -985,19 +989,19 @@ protected:
* @return The transmission status for the transmission. * @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. // 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. // 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); TransmissionStatusType sendRequest(const String &message, const uint8_t *targetBSSID);
transmission_status_t sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID); TransmissionStatusType sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID);
private: private:
EspnowMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, broadcastFilterType broadcastFilter, EspnowMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, broadcastFilterType broadcastFilter,
const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel); const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel);
typedef std::function<String(const String &, const ExpiringTimeTracker &)> encryptionRequestBuilderType; using encryptionRequestBuilderType = std::function<String(const String &, const ExpiringTimeTracker &)>;
static String defaultEncryptionRequestBuilder(const String &requestHeader, const uint32_t durationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker); 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); 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 *getEncryptedConnection(const uint8_t *peerMac);
static EncryptedConnectionLog *getTemporaryEncryptedConnection(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 <typename T> template <typename T>
static typename std::vector<T>::iterator getEncryptedConnectionIterator(const uint8_t *peerMac, typename std::vector<T> &connectionVector); static typename T::iterator getEncryptedConnectionIterator(const uint8_t *peerMac, T &connectionContainer);
static bool getEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator); 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. // @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 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. // 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, // @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 <typename T, typename U> template <typename T, typename U>
static void deleteExpiredLogEntries(std::map<std::pair<U, uint64_t>, T> &logEntries, uint32_t maxEntryLifetimeMs); static void deleteExpiredLogEntries(std::map<std::pair<U, uint64_t>, T> &logEntries, uint32_t maxEntryLifetimeMs);
template <typename U>
static void deleteExpiredLogEntries(std::map<std::pair<U, uint64_t>, TimeTracker> &logEntries, uint32_t maxEntryLifetimeMs);
static void deleteExpiredLogEntries(std::map<std::pair<peerMac_td, messageID_td>, RequestData> &logEntries, uint32_t requestLifetimeMs, uint32_t broadcastLifetimeMs); static void deleteExpiredLogEntries(std::map<std::pair<peerMac_td, messageID_td>, RequestData> &logEntries, uint32_t requestLifetimeMs, uint32_t broadcastLifetimeMs);
template <typename T> template <typename T>
@ -1082,7 +1089,6 @@ private:
static uint32_t _encryptionRequestTimeoutMs; static uint32_t _encryptionRequestTimeoutMs;
static uint32_t _timeOfLastLogClear;
static uint32_t _criticalHeapLevel; static uint32_t _criticalHeapLevel;
static uint32_t _criticalHeapLevelBuffer; static uint32_t _criticalHeapLevelBuffer;
@ -1096,8 +1102,7 @@ private:
static String _ongoingPeerRequestNonce; static String _ongoingPeerRequestNonce;
static uint8_t _ongoingPeerRequestMac[6]; static uint8_t _ongoingPeerRequestMac[6];
static EspnowMeshBackend *_ongoingPeerRequester; static EspnowMeshBackend *_ongoingPeerRequester;
static encrypted_connection_status_t _ongoingPeerRequestResult; static EncryptedConnectionStatus _ongoingPeerRequestResult;
static uint32_t _ongoingPeerRequestEncryptionStart;
static bool _reciprocalPeerRequestConfirmation; static bool _reciprocalPeerRequestConfirmation;
template <typename T> template <typename T>
@ -1119,7 +1124,7 @@ private:
uint8_t _senderAPMac[6] = {0}; uint8_t _senderAPMac[6] = {0};
bool _receivedEncryptedTransmission = false; bool _receivedEncryptedTransmission = false;
static bool _espnowSendToNodeMutex; static std::shared_ptr<bool> _espnowSendToNodeMutex;
static uint8_t _transmissionTargetBSSID[6]; static uint8_t _transmissionTargetBSSID[6];
static void storeSentRequest(const uint64_t targetBSSID, const uint64_t messageID, const RequestData &requestData); 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. * @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. * 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(). * 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. * 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(); static uint64_t createSessionKey();
void prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels); void prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels);
transmission_status_t initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); TransmissionStatusType initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
transmission_status_t initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID); TransmissionStatusType initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID);
void printTransmissionStatistics(); void printTransmissionStatistics();
encrypted_connection_status_t initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection); EncryptedConnectionStatus 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); TransmissionStatusType initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, EncryptedConnectionStatus connectionStatus);
void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, bool requestPermanentConnection); void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, bool requestPermanentConnection);
// Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter // Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter

View File

@ -45,7 +45,7 @@ namespace EspnowProtocolInterpreter
constexpr char basicConnectionInfoHeader[] PROGMEM = "BasicCI:"; // Basic connection info constexpr char basicConnectionInfoHeader[] PROGMEM = "BasicCI:"; // Basic connection info
constexpr char encryptedConnectionInfoHeader[] PROGMEM = "EncryptedCI:"; // Encrypted connection info constexpr char encryptedConnectionInfoHeader[] PROGMEM = "EncryptedCI:"; // Encrypted connection info
constexpr char softLimitEncryptedConnectionInfoHeader[] PROGMEM = "SLEncryptedCI:"; // Soft limit 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 encryptedConnectionVerificationHeader[] PROGMEM = "ECVerified:"; // Encrypted connection verified
constexpr char encryptedConnectionRemovalRequestHeader[] PROGMEM = "RemoveEC:"; // Remove encrypted connection constexpr char encryptedConnectionRemovalRequestHeader[] PROGMEM = "RemoveEC:"; // Remove encrypted connection

View File

@ -24,36 +24,101 @@
#include "ExpiringTimeTracker.h" #include "ExpiringTimeTracker.h"
ExpiringTimeTracker::ExpiringTimeTracker(uint32_t duration, uint32_t creationTimeMs) : ExpiringTimeTracker::ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs) :
TimeTracker(creationTimeMs), _duration(duration) 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 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 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. remainingDuration = duration() - elapsedTime();
return 0;
}
else
{
return remainingDuration;
} }
return remainingDuration;
}
uint32_t ExpiringTimeTracker::elapsedTime() const
{
return millis() - _start;
} }
bool ExpiringTimeTracker::expired() const 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();
} }

View File

@ -25,24 +25,54 @@
#ifndef __EXPIRINGTIMETRACKER_H__ #ifndef __EXPIRINGTIMETRACKER_H__
#define __EXPIRINGTIMETRACKER_H__ #define __EXPIRINGTIMETRACKER_H__
#include "TimeTracker.h"
#include <Arduino.h> #include <Arduino.h>
#include <PolledTimeout.h>
class ExpiringTimeTracker : public TimeTracker { class ExpiringTimeTracker : private esp8266::polledTimeout::oneShotMs {
public: public:
~ExpiringTimeTracker() override = default; using calculatorType = std::function<uint32_t()>;
virtual ~ExpiringTimeTracker() = default;
ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs = millis());
ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs = millis());
ExpiringTimeTracker(uint32_t duration, uint32_t creationTimeMs = millis());
uint32_t duration() const; uint32_t duration() const;
void setRemainingDuration(uint32_t remainingDuration); void setDuration(const uint32_t duration);
void setDuration(const calculatorType durationCalculator);
uint32_t remainingDuration() const; 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; bool expired() const;
void reset();
void reset(const uint32_t newDuration);
void reset(const calculatorType newDurationCalculator);
explicit operator bool() const;
private: private:
uint32_t _duration; bool useCalculator = false;
calculatorType _durationCalculator;
void setTimeout(const uint32_t newUserTimeout);
}; };
#endif #endif

View File

@ -40,13 +40,16 @@ char FloodingMesh::_metadataDelimiter = 23;
void floodingMeshDelay(uint32_t durationMs) void floodingMeshDelay(uint32_t durationMs)
{ {
uint32_t startingTime = millis(); ExpiringTimeTracker timeout(durationMs);
while(millis() - startingTime < 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); delay(1);
FloodingMesh::performMeshMaintenance(); FloodingMesh::performMeshMaintenance();
} }
while(!timeout);
} }
FloodingMesh::FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::espnowEncryptedConnectionKeyLength], 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. * @param meshInstance The MeshBackendBase instance that called the function.
* @return The status code resulting from the response, as an int * @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!"))); 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 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));
if(insertPreliminaryMessageID(messageID))
{ {
int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1); // Add broadcast identifier to stored message and mark as accepted broadcast.
firstTransmission = String(metadataDelimiter()) + firstTransmission;
if(messageIDEndIndex == -1) return true;
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;
}
else
{
return false; // Broadcast has already been received the maximum number of times
}
} }
return false; // Broadcast has already been received the maximum number of times
} }
/** /**

View File

@ -27,7 +27,6 @@
#include "EspnowMeshBackend.h" #include "EspnowMeshBackend.h"
#include <set> #include <set>
#include <unordered_map>
#include <queue> #include <queue>
/** /**
@ -45,8 +44,8 @@ class FloodingMesh {
protected: protected:
typedef std::function<bool(String &, FloodingMesh &)> messageHandlerType; using messageHandlerType = std::function<bool(String &, FloodingMesh &)>;
typedef std::unordered_map<uint64_t, uint8_t>::iterator messageQueueElementType; using messageQueueElementType = std::map<uint64_t, uint8_t>::iterator;
public: public:
@ -61,7 +60,7 @@ public:
* @param ssidSuffix The suffix (last 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 shared by all EspnowMeshBackend instances. * @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. * @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. * 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 * 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 * 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 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 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. * @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. * 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 * 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 * 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}; uint8_t _originMac[6] = {0};
std::unordered_map<uint64_t, uint8_t> _messageIDs = {}; std::map<uint64_t, uint8_t> _messageIDs = {};
std::queue<messageQueueElementType> _messageIdOrder = {}; std::queue<messageQueueElementType> _messageIdOrder = {};
std::list<std::pair<String, bool>> _forwardingBacklog = {}; std::list<std::pair<String, bool>> _forwardingBacklog = {};
String _macIgnoreList; String _macIgnoreList;
String _defaultRequestHandler(const String &request, MeshBackendBase &meshInstance); 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); void _defaultNetworkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance); bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance);
bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance); bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance);

View File

@ -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;
}

View File

@ -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 <Arduino.h>
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

View File

@ -26,11 +26,11 @@ namespace TypeCast = MeshTypeConversionFunctions;
MeshBackendBase *MeshBackendBase::apController = nullptr; MeshBackendBase *MeshBackendBase::apController = nullptr;
bool MeshBackendBase::_scanMutex = false; std::shared_ptr<bool> MeshBackendBase::_scanMutex = std::make_shared<bool>(false);
bool MeshBackendBase::_printWarnings = true; 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); setRequestHandler(requestHandler);
setResponseHandler(responseHandler); setResponseHandler(responseHandler);
@ -43,12 +43,12 @@ MeshBackendBase::~MeshBackendBase()
deactivateControlledAP(); deactivateControlledAP();
} }
void MeshBackendBase::setClassType(mesh_backend_t classType) void MeshBackendBase::setClassType(MeshBackendType classType)
{ {
_classType = classType; _classType = classType;
} }
mesh_backend_t MeshBackendBase::getClassType() {return _classType;} MeshBackendType MeshBackendBase::getClassType() {return _classType;}
void MeshBackendBase::activateAP() void MeshBackendBase::activateAP()
{ {
@ -115,7 +115,9 @@ bool MeshBackendBase::isAPController()
void MeshBackendBase::setWiFiChannel(uint8 newWiFiChannel) 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; _meshWiFiChannel = newWiFiChannel;
@ -248,10 +250,10 @@ bool MeshBackendBase::latestTransmissionSuccessfulBase(const std::vector<Transmi
{ {
if(latestTransmissionOutcomes.empty()) if(latestTransmissionOutcomes.empty())
return false; return false;
else
for(const TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes) for(const TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes)
if(transmissionOutcome.transmissionStatus() != TS_TRANSMISSION_COMPLETE) if(transmissionOutcome.transmissionStatus() != TransmissionStatusType::TRANSMISSION_COMPLETE)
return false; return false;
return true; return true;
} }

View File

@ -23,24 +23,24 @@
#include "TransmissionOutcome.h" #include "TransmissionOutcome.h"
#include "NetworkInfoBase.h" #include "NetworkInfoBase.h"
typedef enum enum class MeshBackendType
{ {
MB_TCP_IP = 0, TCP_IP = 0,
MB_ESP_NOW = 1 ESP_NOW = 1
} mesh_backend_t; };
class MeshBackendBase { class MeshBackendBase {
protected: protected:
typedef std::function<String(const String &, MeshBackendBase &)> requestHandlerType; using requestHandlerType = std::function<String(const String &, MeshBackendBase &)> ;
typedef std::function<transmission_status_t(const String &, MeshBackendBase &)> responseHandlerType; using responseHandlerType = std::function<TransmissionStatusType(const String &, MeshBackendBase &)>;
typedef std::function<void(int, MeshBackendBase &)> networkFilterType; using networkFilterType = std::function<void(int, MeshBackendBase &)>;
typedef std::function<bool(MeshBackendBase &)> transmissionOutcomesUpdateHookType; using transmissionOutcomesUpdateHookType = std::function<bool(MeshBackendBase &)>;
public: public:
MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, mesh_backend_t classType); MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, MeshBackendType classType);
virtual ~MeshBackendBase(); virtual ~MeshBackendBase();
@ -109,13 +109,13 @@ public:
* Will also change the WiFi channel for the active AP (via an AP restart) * 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. * 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. * 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 * 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 * 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. * 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); void setWiFiChannel(uint8 newWiFiChannel);
@ -284,14 +284,14 @@ public:
*/ */
static void warningPrint(const String &stringToPrint, bool newline = true); static void warningPrint(const String &stringToPrint, bool newline = true);
mesh_backend_t getClassType(); MeshBackendType getClassType();
protected: protected:
/** /**
* @param latestTransmissionOutcomes The transmission outcomes vector to check. * @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<TransmissionOutcome> &latestTransmissionOutcomes); static bool latestTransmissionSuccessfulBase(const std::vector<TransmissionOutcome> &latestTransmissionOutcomes);
@ -310,13 +310,13 @@ protected:
*/ */
virtual void deactivateAPHook(); virtual void deactivateAPHook();
void setClassType(mesh_backend_t classType); void setClassType(MeshBackendType classType);
static bool _scanMutex; static std::shared_ptr<bool> _scanMutex;
private: private:
mesh_backend_t _classType; MeshBackendType _classType;
static MeshBackendBase *apController; static MeshBackendBase *apController;

View File

@ -28,15 +28,15 @@
#include <assert.h> #include <assert.h>
MessageData::MessageData(String &message, uint8_t transmissionsRemaining, uint32_t creationTimeMs) : MessageData::MessageData(String &message, uint8_t transmissionsRemaining, uint32_t creationTimeMs) :
TimeTracker(creationTimeMs) _timeTracker(creationTimeMs)
{ {
_transmissionsExpected = transmissionsRemaining + 1; _transmissionsExpected = transmissionsRemaining + 1;
_totalMessage += message; _totalMessage += message;
_transmissionsReceived++; ++_transmissionsReceived;
} }
MessageData::MessageData(uint8_t *initialTransmission, uint8_t transmissionLength, uint32_t creationTimeMs) : MessageData::MessageData(uint8_t *initialTransmission, uint8_t transmissionLength, uint32_t creationTimeMs) :
TimeTracker(creationTimeMs) _timeTracker(creationTimeMs)
{ {
_transmissionsExpected = EspnowProtocolInterpreter::espnowGetTransmissionsRemaining(initialTransmission) + 1; _transmissionsExpected = EspnowProtocolInterpreter::espnowGetTransmissionsRemaining(initialTransmission) + 1;
addToMessage(initialTransmission, transmissionLength); addToMessage(initialTransmission, transmissionLength);
@ -49,7 +49,7 @@ bool MessageData::addToMessage(uint8_t *transmission, uint8_t transmissionLength
String message = EspnowProtocolInterpreter::espnowGetMessageContent(transmission, transmissionLength); String message = EspnowProtocolInterpreter::espnowGetMessageContent(transmission, transmissionLength);
assert(message.length() <= EspnowMeshBackend::getMaxMessageBytesPerTransmission()); // Should catch some cases where transmission is not null terminated. assert(message.length() <= EspnowMeshBackend::getMaxMessageBytesPerTransmission()); // Should catch some cases where transmission is not null terminated.
_totalMessage += message; _totalMessage += message;
_transmissionsReceived++; ++_transmissionsReceived;
return true; return true;
} }
@ -75,3 +75,5 @@ String MessageData::getTotalMessage()
{ {
return _totalMessage; return _totalMessage;
} }
const TimeTracker &MessageData::getTimeTracker() const { return _timeTracker; }

View File

@ -28,7 +28,7 @@
#include "TimeTracker.h" #include "TimeTracker.h"
#include <Arduino.h> #include <Arduino.h>
class MessageData : public TimeTracker { class MessageData {
public: public:
@ -43,9 +43,11 @@ public:
uint8_t getTransmissionsExpected(); uint8_t getTransmissionsExpected();
uint8_t getTransmissionsRemaining(); uint8_t getTransmissionsRemaining();
String getTotalMessage(); String getTotalMessage();
const TimeTracker &getTimeTracker() const;
private: private:
TimeTracker _timeTracker;
uint8_t _transmissionsReceived = 0; uint8_t _transmissionsReceived = 0;
uint8_t _transmissionsExpected; uint8_t _transmissionsExpected;
String _totalMessage; String _totalMessage;

View File

@ -24,19 +24,19 @@
#include "MutexTracker.h" #include "MutexTracker.h"
bool MutexTracker::_captureBan = false; std::shared_ptr<bool> MutexTracker::_captureBan = std::make_shared<bool>(false);
bool &MutexTracker::captureBan() std::shared_ptr<bool> MutexTracker::captureBan()
{ {
return _captureBan; return _captureBan;
} }
MutexTracker::MutexTracker(bool &mutexToCapture) MutexTracker::MutexTracker(const std::shared_ptr<bool> &mutexToCapture)
{ {
attemptMutexCapture(mutexToCapture); attemptMutexCapture(mutexToCapture);
} }
MutexTracker::MutexTracker(bool &mutexToCapture, std::function<void()> destructorHook) : MutexTracker(mutexToCapture) MutexTracker::MutexTracker(const std::shared_ptr<bool> &mutexToCapture, const std::function<void()> destructorHook) : MutexTracker(mutexToCapture)
{ {
_destructorHook = destructorHook; _destructorHook = destructorHook;
} }
@ -47,12 +47,25 @@ MutexTracker::~MutexTracker()
_destructorHook(); _destructorHook();
} }
bool MutexTracker::mutexCaptured() bool MutexTracker::mutexFree(const std::shared_ptr<bool> &mutex)
{ {
if(_capturedMutex) if(mutex != nullptr && !(*mutex))
return true; return true;
else
return false; return false;
}
bool MutexTracker::mutexCaptured(const std::shared_ptr<bool> &mutex)
{
if(mutex != nullptr && (*mutex))
return true;
return false;
}
bool MutexTracker::mutexCaptured() const
{
return mutexCaptured(_capturedMutex);
} }
void MutexTracker::releaseMutex() void MutexTracker::releaseMutex()
@ -60,20 +73,18 @@ void MutexTracker::releaseMutex()
if(mutexCaptured()) if(mutexCaptured())
{ {
*_capturedMutex = false; *_capturedMutex = false;
_capturedMutex = nullptr; _capturedMutex.reset();
} }
} }
bool MutexTracker::attemptMutexCapture(bool &mutexToCapture) bool MutexTracker::attemptMutexCapture(const std::shared_ptr<bool> &mutexToCapture)
{ {
if(!captureBan() && !mutexToCapture) if(mutexFree(captureBan()) && mutexFree(mutexToCapture))
{ {
_capturedMutex = &mutexToCapture; _capturedMutex = mutexToCapture;
*_capturedMutex = true; *_capturedMutex = true;
return true; return true;
} }
else
{ return false;
return false;
}
} }

View File

@ -26,6 +26,7 @@
#define __MUTEXTRACKER_H__ #define __MUTEXTRACKER_H__
#include <functional> #include <functional>
#include <memory>
/** /**
* A SLIM (Scope LImited Manager)/Scope-Bound Resource Management/RAII class to manage the state of a mutex. * 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. * Set to false by default.
* captureBan can be managed by MutexTracker like any other mutex. * captureBan can be managed by MutexTracker like any other mutex.
*/ */
static bool &captureBan(); static std::shared_ptr<bool> captureBan();
/** /**
* Attempts to capture the mutex. Use the mutexCaptured() method to check success. * Attempts to capture the mutex. Use the mutexCaptured() method to check success.
*/ */
MutexTracker(bool &mutexToCapture); MutexTracker(const std::shared_ptr<bool> &mutexToCapture);
/** /**
* Attempts to capture the mutex. Use the mutexCaptured() method to check success. * 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. * @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<void()> destructorHook); MutexTracker(const std::shared_ptr<bool> &mutexToCapture, const std::function<void()> destructorHook);
~MutexTracker(); ~MutexTracker();
bool mutexCaptured(); bool mutexCaptured() const;
/** /**
* Set the mutex free to roam the binary plains, giving new MutexTrackers a chance to capture it. * Set the mutex free to roam the binary plains, giving new MutexTrackers a chance to capture it.
@ -64,17 +65,20 @@ class MutexTracker
private: private:
static bool _captureBan; static std::shared_ptr<bool> _captureBan;
bool *_capturedMutex = nullptr; std::shared_ptr<bool> _capturedMutex;
std::function<void()> _destructorHook = [](){ }; std::function<void()> _destructorHook = [](){ };
static bool mutexFree(const std::shared_ptr<bool> &mutex);
static bool mutexCaptured(const std::shared_ptr<bool> &mutex);
/** /**
* Attempt to capture the 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<bool> &mutexToCapture);
}; };
#endif #endif

View File

@ -40,7 +40,7 @@ void NetworkInfoBase::storeBSSID(const uint8_t newBSSID[6])
_BSSID = _bssidArray; _BSSID = _bssidArray;
} }
for(int i = 0; i < 6; i++) for(int i = 0; i < 6; ++i)
{ {
_BSSID[i] = newBSSID[i]; _BSSID[i] = newBSSID[i];
} }

View File

@ -25,8 +25,9 @@
#include "RequestData.h" #include "RequestData.h"
RequestData::RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs) : RequestData::RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs) :
TimeTracker(creationTimeMs), _meshInstance(meshInstance) _timeTracker(creationTimeMs), _meshInstance(meshInstance)
{ } { }
void RequestData::setMeshInstance(EspnowMeshBackend &meshInstance) { _meshInstance = meshInstance; } void RequestData::setMeshInstance(const EspnowMeshBackend &meshInstance) { _meshInstance = meshInstance; }
EspnowMeshBackend &RequestData::getMeshInstance() { return _meshInstance; } EspnowMeshBackend &RequestData::getMeshInstance() const { return _meshInstance; }
const TimeTracker &RequestData::getTimeTracker() const { return _timeTracker; }

View File

@ -30,17 +30,19 @@
class EspnowMeshBackend; class EspnowMeshBackend;
class RequestData : public TimeTracker { class RequestData {
public: public:
RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs = millis()); RequestData(EspnowMeshBackend &meshInstance, uint32_t creationTimeMs = millis());
void setMeshInstance(EspnowMeshBackend &meshInstance); void setMeshInstance(const EspnowMeshBackend &meshInstance);
EspnowMeshBackend &getMeshInstance(); EspnowMeshBackend &getMeshInstance() const;
const TimeTracker &getTimeTracker() const;
private: private:
TimeTracker _timeTracker;
EspnowMeshBackend &_meshInstance; EspnowMeshBackend &_meshInstance;
}; };

View File

@ -25,13 +25,13 @@
#include "ResponseData.h" #include "ResponseData.h"
ResponseData::ResponseData(const String &message, const uint8_t recipientMac[6], uint64_t requestID, uint32_t creationTimeMs) : 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); storeRecipientMac(recipientMac);
} }
ResponseData::ResponseData(const ResponseData &other) 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()); storeRecipientMac(other.getRecipientMac());
} }
@ -40,7 +40,7 @@ ResponseData & ResponseData::operator=(const ResponseData &other)
{ {
if(this != &other) if(this != &other)
{ {
TimeTracker::operator=(other); _timeTracker = other.getTimeTracker();
_message = other.getMessage(); _message = other.getMessage();
_requestID = other.getRequestID(); _requestID = other.getRequestID();
storeRecipientMac(other.getRecipientMac()); storeRecipientMac(other.getRecipientMac());
@ -51,21 +51,20 @@ ResponseData & ResponseData::operator=(const ResponseData &other)
void ResponseData::storeRecipientMac(const uint8_t newRecipientMac[6]) void ResponseData::storeRecipientMac(const uint8_t newRecipientMac[6])
{ {
if(newRecipientMac != nullptr) if(newRecipientMac == nullptr)
{
if(_recipientMac == nullptr)
{
_recipientMac = _recipientMacArray;
}
for(int i = 0; i < 6; i++)
{
_recipientMac[i] = newRecipientMac[i];
}
}
else
{ {
_recipientMac = 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; } void ResponseData::setRequestID(uint64_t requestID) { _requestID = requestID; }
uint64_t ResponseData::getRequestID() const { return _requestID; } uint64_t ResponseData::getRequestID() const { return _requestID; }
const TimeTracker &ResponseData::getTimeTracker() const { return _timeTracker; }

View File

@ -28,7 +28,7 @@
#include "TimeTracker.h" #include "TimeTracker.h"
#include <Arduino.h> #include <Arduino.h>
class ResponseData : public TimeTracker { class ResponseData {
public: public:
@ -46,10 +46,14 @@ public:
void setRequestID(uint64_t requestID); void setRequestID(uint64_t requestID);
uint64_t getRequestID() const; uint64_t getRequestID() const;
const TimeTracker &getTimeTracker() const;
private: private:
void storeRecipientMac(const uint8_t newRecipientMac[6]); void storeRecipientMac(const uint8_t newRecipientMac[6]);
TimeTracker _timeTracker;
uint8_t _recipientMacArray[6] {0}; uint8_t _recipientMacArray[6] {0};
uint8_t *_recipientMac = nullptr; uint8_t *_recipientMac = nullptr;
String _message; String _message;

View File

@ -32,8 +32,8 @@ namespace
const IPAddress TcpIpMeshBackend::emptyIP; const IPAddress TcpIpMeshBackend::emptyIP;
bool TcpIpMeshBackend::_tcpIpTransmissionMutex = false; std::shared_ptr<bool> TcpIpMeshBackend::_tcpIpTransmissionMutex = std::make_shared<bool>(false);
bool TcpIpMeshBackend::_tcpIpConnectionQueueMutex = false; std::shared_ptr<bool> TcpIpMeshBackend::_tcpIpConnectionQueueMutex = std::make_shared<bool>(false);
String TcpIpMeshBackend::lastSSID; String TcpIpMeshBackend::lastSSID;
bool TcpIpMeshBackend::staticIPActivated = false; bool TcpIpMeshBackend::staticIPActivated = false;
@ -51,7 +51,7 @@ std::vector<TransmissionOutcome> TcpIpMeshBackend::_latestTransmissionOutcomes =
TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler,
networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix, networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix,
const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) 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); setSSID(ssidPrefix, emptyString, ssidSuffix);
setMeshPassword(meshPassword); setMeshPassword(meshPassword);
@ -111,7 +111,7 @@ void TcpIpMeshBackend::deactivateAPHook()
_server.stop(); _server.stop();
} }
bool TcpIpMeshBackend::transmissionInProgress(){return _tcpIpTransmissionMutex;} bool TcpIpMeshBackend::transmissionInProgress(){return *_tcpIpTransmissionMutex;}
void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;} void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;}
String TcpIpMeshBackend::getTemporaryMessage() {return _temporaryMessage;} String TcpIpMeshBackend::getTemporaryMessage() {return _temporaryMessage;}
@ -180,12 +180,12 @@ void TcpIpMeshBackend::setMaxAPStations(uint8_t maxAPStations)
bool TcpIpMeshBackend::getMaxAPStations() {return _maxAPStations;} bool TcpIpMeshBackend::getMaxAPStations() {return _maxAPStations;}
void TcpIpMeshBackend::setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs) void TcpIpMeshBackend::setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs)
{ {
_connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; _connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
} }
int32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;} uint32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;}
void TcpIpMeshBackend::setStationModeTimeout(int stationModeTimeoutMs) void TcpIpMeshBackend::setStationModeTimeout(int stationModeTimeoutMs)
{ {
@ -220,12 +220,11 @@ void TcpIpMeshBackend::fullStop(WiFiClient &currClient)
*/ */
bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait) bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait)
{ {
uint32_t connectionStartTime = millis(); ExpiringTimeTracker timeout(maxWait);
uint32_t waitingTime = millis() - connectionStartTime;
while(currClient.connected() && !currClient.available() && waitingTime < maxWait) while(currClient.connected() && !currClient.available() && !timeout)
{ {
delay(1); delay(1);
waitingTime = millis() - connectionStartTime;
} }
/* Return false if the client isn't ready to communicate */ /* 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. * @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"))); verboseModePrint(String(F("Transmitting")));
@ -256,13 +255,13 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) if (!waitForClientTransmission(currClient, _stationModeTimeoutMs))
{ {
fullStop(currClient); fullStop(currClient);
return TS_CONNECTION_FAILED; return TransmissionStatusType::CONNECTION_FAILED;
} }
if (!currClient.available()) if (!currClient.available())
{ {
verboseModePrint(F("No response!")); 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'); 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. * @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. // 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. // 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(); WiFiMode_t storedWiFiMode = WiFi.getMode();
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
delay(1); delay(1);
transmission_status_t transmissionOutcome = attemptDataTransferKernel(); TransmissionStatusType transmissionOutcome = attemptDataTransferKernel();
WiFi.mode(storedWiFiMode); WiFi.mode(storedWiFiMode);
delay(1); delay(1);
@ -298,7 +297,7 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransfer()
* *
* @return A status code based on the outcome of the data transfer attempt. * @return A status code based on the outcome of the data transfer attempt.
*/ */
transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel() TransmissionStatusType TcpIpMeshBackend::attemptDataTransferKernel()
{ {
WiFiClient currClient; WiFiClient currClient;
currClient.setTimeout(_stationModeTimeoutMs); currClient.setTimeout(_stationModeTimeoutMs);
@ -308,11 +307,11 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel()
{ {
fullStop(currClient); fullStop(currClient);
verboseModePrint(F("Server unavailable")); verboseModePrint(F("Server unavailable"));
return TS_CONNECTION_FAILED; return TransmissionStatusType::CONNECTION_FAILED;
} }
transmission_status_t transmissionOutcome = exchangeInfo(currClient); TransmissionStatusType transmissionOutcome = exchangeInfo(currClient);
if (transmissionOutcome <= 0) if (static_cast<int>(transmissionOutcome) <= 0)
{ {
verboseModePrint(F("Transmission failed during exchangeInfo.")); verboseModePrint(F("Transmission failed during exchangeInfo."));
return transmissionOutcome; 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. * @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. 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); verboseModePrint(F("Connecting... "), false);
initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); initiateConnectionToAP(targetSSID, targetChannel, targetBSSID);
int connectionStartTime = millis();
int attemptNumber = 1; int attemptNumber = 1;
ExpiringTimeTracker connectionAttemptTimeout([this](){ return _connectionAttemptTimeoutMs; });
int waitingTime = millis() - connectionStartTime; while((WiFi.status() == WL_DISCONNECTED) && !connectionAttemptTimeout)
while((WiFi.status() == WL_DISCONNECTED) && waitingTime <= _connectionAttemptTimeoutMs)
{ {
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); verboseModePrint(F("... "), false);
WiFi.disconnect(); WiFi.disconnect();
yield(); yield();
initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); initiateConnectionToAP(targetSSID, targetChannel, targetBSSID);
attemptNumber++; ++attemptNumber;
} }
delay(1); delay(1);
waitingTime = millis() - connectionStartTime;
} }
verboseModePrint(String(waitingTime)); verboseModePrint(String(connectionAttemptTimeout.elapsedTime()));
/* If the connection timed out */ /* If the connection timed out */
if (WiFi.status() != WL_CONNECTED) if (WiFi.status() != WL_CONNECTED)
{ {
verboseModePrint(F("Timeout")); verboseModePrint(F("Timeout"));
return TS_CONNECTION_FAILED; return TransmissionStatusType::CONNECTION_FAILED;
} }
return attemptDataTransfer(); return attemptDataTransfer();
} }
transmission_status_t TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo) TransmissionStatusType TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo)
{ {
WiFi.disconnect(); WiFi.disconnect();
yield(); yield();
@ -452,7 +450,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
if(WiFi.status() == WL_CONNECTED) if(WiFi.status() == WL_CONNECTED)
{ {
transmission_status_t transmissionResult = attemptDataTransfer(); TransmissionStatusType transmissionResult = attemptDataTransfer();
latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult)); latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult));
getTransmissionOutcomesUpdateHook()(*this); getTransmissionOutcomesUpdateHook()(*this);
@ -474,7 +472,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
{ {
for(const TcpIpNetworkInfo &currentNetwork : constConnectionQueue()) for(const TcpIpNetworkInfo &currentNetwork : constConnectionQueue())
{ {
transmission_status_t transmissionResult = initiateTransmission(currentNetwork); TransmissionStatusType transmissionResult = initiateTransmission(currentNetwork);
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); 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); 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); MutexTracker mutexTracker(_tcpIpTransmissionMutex);
if(!mutexTracker.mutexCaptured()) 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."))); 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); setTemporaryMessage(message);
if(initialDisconnect) if(initialDisconnect)

View File

@ -43,14 +43,14 @@ public:
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which * @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. * 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 * @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 networkFilter The callback handler for deciding which WiFi networks to connect to.
* @param meshPassword The WiFi password for the mesh network. * @param meshPassword The WiFi password for the mesh network.
* @param ssidPrefix The prefix (first part) of the node SSID. * @param ssidPrefix The prefix (first part) of the node SSID.
* @param ssidSuffix The suffix (last 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 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. * @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. * 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 * 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 * 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<TransmissionOutcome> & latestTransmissionOutcomes(); static std::vector<TransmissionOutcome> & 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. * The result is unique for each mesh backend.
*/ */
static bool latestTransmissionSuccessful(); static bool latestTransmissionSuccessful();
@ -124,7 +124,7 @@ public:
* *
* Note that if wifiChannel and BSSID are missing from recipientInfo, connection time will be longer. * 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. * 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. * @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds.
*/ */
void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs); void setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs);
int32_t getConnectionAttemptTimeout(); 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). * 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. * Will be true if a transmission initiated by a public method is in progress.
*/ */
static bool _tcpIpTransmissionMutex; static std::shared_ptr<bool> _tcpIpTransmissionMutex;
/** /**
* Will be true when the connectionQueue should not be modified. * Will be true when the connectionQueue should not be modified.
*/ */
static bool _tcpIpConnectionQueueMutex; static std::shared_ptr<bool> _tcpIpConnectionQueueMutex;
/** /**
* Check if there is an ongoing TCP/IP transmission in the library. Used to avoid interrupting transmissions. * 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; uint16_t _serverPort;
WiFiServer _server; WiFiServer _server;
uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections 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. 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; uint32_t _apModeTimeoutMs = 4500;
@ -271,12 +271,12 @@ private:
void fullStop(WiFiClient &currClient); void fullStop(WiFiClient &currClient);
void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); 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); TransmissionStatusType connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL);
transmission_status_t exchangeInfo(WiFiClient &currClient); TransmissionStatusType exchangeInfo(WiFiClient &currClient);
bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait);
transmission_status_t attemptDataTransfer(); TransmissionStatusType attemptDataTransfer();
transmission_status_t attemptDataTransferKernel(); TransmissionStatusType attemptDataTransferKernel();
transmission_status_t initiateTransmission(const TcpIpNetworkInfo &recipientInfo); TransmissionStatusType initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
void enterPostTransmissionState(bool concludingDisconnect); void enterPostTransmissionState(bool concludingDisconnect);
}; };

View File

@ -27,6 +27,7 @@
#include <stdint.h> #include <stdint.h>
// Minimal time tracking class. Used instead of other classes like ExpiringTimeTracker when small memory footprint is important and other functionality not required.
class TimeTracker { class TimeTracker {
public: public:

View File

@ -24,13 +24,13 @@
#include "TransmissionOutcome.h" #include "TransmissionOutcome.h"
TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t transmissionStatus) TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, TransmissionStatusType transmissionStatus)
: NetworkInfoBase(origin), _transmissionStatus(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) : NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden), _transmissionStatus(transmissionStatus)
{ } { }
void TransmissionOutcome::setTransmissionStatus(transmission_status_t transmissionStatus) { _transmissionStatus = transmissionStatus; } void TransmissionOutcome::setTransmissionStatus(TransmissionStatusType transmissionStatus) { _transmissionStatus = transmissionStatus; }
transmission_status_t TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; } TransmissionStatusType TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; }

View File

@ -28,27 +28,27 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "NetworkInfoBase.h" #include "NetworkInfoBase.h"
typedef enum enum class TransmissionStatusType
{ {
TS_CONNECTION_FAILED = -1, CONNECTION_FAILED = -1,
TS_TRANSMISSION_FAILED = 0, TRANSMISSION_FAILED = 0,
TS_TRANSMISSION_COMPLETE = 1 TRANSMISSION_COMPLETE = 1
} transmission_status_t; };
class TransmissionOutcome : public NetworkInfoBase { class TransmissionOutcome : public NetworkInfoBase {
public: 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); void setTransmissionStatus(TransmissionStatusType transmissionStatus);
transmission_status_t transmissionStatus() const; TransmissionStatusType transmissionStatus() const;
private: private:
transmission_status_t _transmissionStatus; TransmissionStatusType _transmissionStatus;
}; };
#endif #endif

View File

@ -54,6 +54,13 @@
#include "NetworkInfo.h" #include "NetworkInfo.h"
#include "TransmissionOutcome.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 { class TransmissionResult : public NetworkInfo {
public: public:
@ -63,11 +70,11 @@ public:
/** /**
* @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. * @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); TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus);
}; };

View File

@ -27,15 +27,15 @@
namespace 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 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] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 10 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, 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 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 namespace MeshTypeConversionFunctions
{ {
String uint64ToString(uint64_t number, byte base) String uint64ToString(uint64_t number, const byte base)
{ {
assert(2 <= base && base <= 36); assert(2 <= base && base <= 36);
@ -44,14 +44,14 @@ namespace MeshTypeConversionFunctions
if(base == 16) if(base == 16)
{ {
do { 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. 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 ); } while ( number );
} }
else else
{ {
do { do {
result += chars[ number % base ]; result += (char)pgm_read_byte(chars + number % base);
number /= base; number /= base;
} while ( number ); } while ( number );
} }
@ -61,7 +61,7 @@ namespace MeshTypeConversionFunctions
return result; return result;
} }
uint64_t stringToUint64(const String &string, byte base) uint64_t stringToUint64(const String &string, const byte base)
{ {
assert(2 <= base && base <= 36); assert(2 <= base && base <= 36);
@ -72,7 +72,7 @@ namespace MeshTypeConversionFunctions
for(uint32_t i = 0; i < string.length(); ++i) 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 <<= 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 else
@ -80,14 +80,14 @@ namespace MeshTypeConversionFunctions
for(uint32_t i = 0; i < string.length(); ++i) for(uint32_t i = 0; i < string.length(); ++i)
{ {
result *= base; result *= base;
result += charValues[string.charAt(i) - '0']; result += pgm_read_byte(charValues + string.charAt(i) - '0');
} }
} }
return result; return result;
} }
String uint8ArrayToHexString(const uint8_t *uint8Array, uint32_t arrayLength) String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength)
{ {
String hexString; String hexString;
if(!hexString.reserve(2*arrayLength)) // Each uint8_t will become two characters (00 to FF) 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) for(uint32_t i = 0; i < arrayLength; ++i)
{ {
hexString += chars[ uint8Array[i] >> 4 ]; hexString += (char)pgm_read_byte(chars + (uint8Array[i] >> 4));
hexString += chars[ uint8Array[i] % 16 ]; hexString += (char)pgm_read_byte(chars + uint8Array[i] % 16 );
} }
return hexString; 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 assert(hexString.length() >= arrayLength*2); // Each array element can hold two hexString characters
for(uint32_t i = 0; i < arrayLength; ++i) 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; return uint8Array;
} }
String uint8ArrayToMultiString(uint8_t *uint8Array, uint32_t arrayLength) String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength)
{ {
String multiString; String multiString;
if(!multiString.reserve(arrayLength)) if(!multiString.reserve(arrayLength))
@ -137,7 +137,7 @@ namespace MeshTypeConversionFunctions
return multiString; return multiString;
} }
String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, uint32_t arrayLength) String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength)
{ {
String multiString; String multiString;
if(!multiString.reserve(arrayLength)) if(!multiString.reserve(arrayLength))
@ -174,7 +174,7 @@ namespace MeshTypeConversionFunctions
return result; 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 assert(macValue <= 0xFFFFFFFFFFFF); // Overflow will occur if value can't fit within 6 bytes
@ -188,7 +188,7 @@ namespace MeshTypeConversionFunctions
return macArray; 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[7] = value;
resultArray[6] = value >> 8; resultArray[6] = value >> 8;
@ -214,27 +214,25 @@ namespace MeshTypeConversionFunctions
* Helper function for meshBackendCast. * Helper function for meshBackendCast.
*/ */
template <typename T> template <typename T>
T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, mesh_backend_t resultClassType) T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, MeshBackendType resultClassType)
{ {
if(meshBackendBaseInstance && meshBackendBaseInstance->getClassType() == resultClassType) if(meshBackendBaseInstance && meshBackendBaseInstance->getClassType() == resultClassType)
{ {
return static_cast<T>(meshBackendBaseInstance); return static_cast<T>(meshBackendBaseInstance);
} }
else
{ return nullptr;
return nullptr;
}
} }
template <> template <>
EspnowMeshBackend *meshBackendCast<EspnowMeshBackend *>(MeshBackendBase *meshBackendBaseInstance) EspnowMeshBackend *meshBackendCast<EspnowMeshBackend *>(MeshBackendBase *meshBackendBaseInstance)
{ {
return attemptPointerCast<EspnowMeshBackend *>(meshBackendBaseInstance, MB_ESP_NOW); return attemptPointerCast<EspnowMeshBackend *>(meshBackendBaseInstance, MeshBackendType::ESP_NOW);
} }
template <> template <>
TcpIpMeshBackend *meshBackendCast<TcpIpMeshBackend *>(MeshBackendBase *meshBackendBaseInstance) TcpIpMeshBackend *meshBackendCast<TcpIpMeshBackend *>(MeshBackendBase *meshBackendBaseInstance)
{ {
return attemptPointerCast<TcpIpMeshBackend *>(meshBackendBaseInstance, MB_TCP_IP); return attemptPointerCast<TcpIpMeshBackend *>(meshBackendBaseInstance, MeshBackendType::TCP_IP);
} }
} }

View File

@ -42,7 +42,7 @@ namespace MeshTypeConversionFunctions
* @param base The radix to convert "number" into. Must be between 2 and 36. * @param base The radix to convert "number" into. Must be between 2 and 36.
* @return A string of "number" encoded in radix "base". * @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. * 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. * @param base The radix of "string". Must be between 2 and 36.
* @return A uint64_t of the string, using radix "base" during decoding. * @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. * 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. * @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. * @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. * 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. * @param arrayLength The number of bytes to fill in uint8Array.
* @return A pointer to the 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. * 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. * @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. * @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. * 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. * @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. * @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. * 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. * @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. * @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. * 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. * @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. * @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. * 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.

View File

@ -30,7 +30,7 @@ namespace MeshUtilityFunctions
{ {
bool macEqual(const uint8_t *macOne, const uint8_t *macTwo) 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]) if(macOne[i] != macTwo[i])
{ {