1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-25 20:02:37 +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 <EspnowMeshBackend.h>
@ -40,7 +40,7 @@ unsigned int responseNumber = 0;
const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII
String manageRequest(const String &request, MeshBackendBase &meshInstance);
transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance);
TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance);
void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance);
@ -55,27 +55,22 @@ EspnowMeshBackend espnowNode = EspnowMeshBackend(manageRequest, manageResponse,
@return The string to send back to the other node. For ESP-NOW, return an empy string ("") if no response should be sent.
*/
String manageRequest(const String &request, MeshBackendBase &meshInstance) {
// We do not store strings in flash (via F()) in this function.
// The reason is that the other node will be waiting for our response,
// so keeping the strings in RAM will give a (small) improvement in response time.
// Of course, it is advised to adjust this approach based on RAM requirements.
// To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled)
if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): ");
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission");
Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): "));
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
(void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else.
Serial.print("TCP/IP: ");
Serial.print(F("TCP/IP: "));
} else {
Serial.print("UNKNOWN!: ");
Serial.print(F("UNKNOWN!: "));
}
/* Print out received message */
// Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function.
// If you need to print the whole String it is better to store it and print it in the loop() later.
// Note that request.substring will not work as expected if the String contains null values as data.
Serial.print("Request received: ");
Serial.print(F("Request received: "));
if (request.charAt(0) == 0) {
Serial.println(request); // substring will not work for multiStrings.
@ -84,7 +79,7 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) {
}
/* return a string to send back */
return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + " with AP MAC " + WiFi.softAPmacAddress() + ".");
return (String(F("Hello world response #")) + String(responseNumber++) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + F(" with AP MAC ") + WiFi.softAPmacAddress() + String('.'));
}
/**
@ -94,15 +89,15 @@ String manageRequest(const String &request, MeshBackendBase &meshInstance) {
@param meshInstance The MeshBackendBase instance that called the function.
@return The status code resulting from the response, as an int
*/
transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance) {
transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE;
TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) {
TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE;
// To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled)
if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? ", Encrypted transmission" : ", Unencrypted transmission";
Serial.print("ESP-NOW (" + espnowInstance->getSenderMac() + transmissionEncrypted + "): ");
String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission");
Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): "));
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<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.
// With TCP/IP the response will follow immediately after the request, so the stored message will not have changed.
@ -111,7 +106,7 @@ transmission_status_t manageResponse(const String &response, MeshBackendBase &me
Serial.print(F("Request sent: "));
Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100));
} else {
Serial.print("UNKNOWN!: ");
Serial.print(F("UNKNOWN!: "));
}
/* Print out received message */
@ -146,7 +141,7 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
} else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
tcpIpInstance->connectionQueue().push_back(networkIndex);
} else {
Serial.println(String(F("Invalid mesh backend!")));
Serial.println(F("Invalid mesh backend!"));
}
}
}
@ -177,7 +172,7 @@ bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance)
String targetMeshName = firstTransmission.substring(0, metadataEndIndex);
if (targetMeshName != "" && meshInstance.getMeshName() != targetMeshName) {
if (!targetMeshName.isEmpty() && meshInstance.getMeshName() != targetMeshName) {
return false; // Broadcast is for another mesh network
} else {
// Remove metadata from message and mark as accepted broadcast.
@ -274,7 +269,7 @@ void setup() {
// Storing our message in the EspnowMeshBackend instance is not required, but can be useful for organizing code, especially when using many EspnowMeshBackend instances.
// Note that calling the multi-recipient versions of espnowNode.attemptTransmission and espnowNode.attemptAutoEncryptingTransmission will replace the stored message with whatever message is transmitted.
// Also note that the maximum allowed number of ASCII characters in a ESP-NOW message is given by EspnowMeshBackend::getMaxMessageLength().
espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F(".")));
espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'));
espnowNode.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook);
espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook);
@ -290,7 +285,7 @@ void setup() {
// Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received.
// All nodes this node wishes to communicate with must then also use encrypted messages with the same getEspnowMessageEncryptionKey(), or messages will not be accepted.
// Note that using AEAD encrypted messages will reduce the number of message bytes that can be transmitted.
//espnowNode.setEspnowMessageEncryptionKey("ChangeThisKeySeed_TODO"); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used.
//espnowNode.setEspnowMessageEncryptionKey(F("ChangeThisKeySeed_TODO")); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used.
//espnowNode.setUseEncryptedMessages(true);
}
@ -306,11 +301,11 @@ void loop() {
EspnowMeshBackend::performEspnowMaintenance();
if (millis() - timeOfLastScan > 10000) { // Give other nodes some time to connect between data transfers.
Serial.println("\nPerforming unencrypted ESP-NOW transmissions.");
Serial.println(F("\nPerforming unencrypted ESP-NOW transmissions."));
uint32_t startTime = millis();
espnowNode.attemptTransmission(espnowNode.getMessage());
Serial.println("Scan and " + String(espnowNode.latestTransmissionOutcomes().size()) + " transmissions done in " + String(millis() - startTime) + " ms.");
Serial.println(String(F("Scan and ")) + String(espnowNode.latestTransmissionOutcomes().size()) + F(" transmissions done in ") + String(millis() - startTime) + F(" ms."));
timeOfLastScan = millis();
@ -328,19 +323,19 @@ void loop() {
Serial.println(F("No mesh AP found."));
} else {
for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) {
if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_FAILED) {
if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_FAILED) {
Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionOutcome.transmissionStatus() == TS_CONNECTION_FAILED) {
} else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::CONNECTION_FAILED) {
Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_COMPLETE) {
} else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) {
// No need to do anything, transmission was successful.
} else {
Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String(F("!")));
Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!'));
assert(F("Invalid transmission status returned from responseHandler!") && false);
}
}
Serial.println("\nPerforming ESP-NOW broadcast.");
Serial.println(F("\nPerforming ESP-NOW broadcast."));
startTime = millis();
@ -348,9 +343,9 @@ void loop() {
// Note that data that comes before broadcastMetadataDelimiter should not contain any broadcastMetadataDelimiter characters,
// otherwise the broadcastFilter function used in this example file will not work.
String broadcastMetadata = espnowNode.getMeshName() + String(broadcastMetadataDelimiter);
String broadcastMessage = String(F("Broadcast #")) + String(requestNumber) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F("."));
String broadcastMessage = String(F("Broadcast #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.');
espnowNode.broadcast(broadcastMetadata + broadcastMessage);
Serial.println("Broadcast to all mesh nodes done in " + String(millis() - startTime) + " ms.");
Serial.println(String(F("Broadcast to all mesh nodes done in ")) + String(millis() - startTime) + F(" ms."));
espnowDelay(100); // Wait for responses (broadcasts can receive an unlimited number of responses, other transmissions can only receive one response).
@ -358,25 +353,25 @@ void loop() {
// You can use String::c_str() or String::begin() to retreive the data array later.
// Note that certain String methods such as String::substring use null values to determine String length, which means they will not work as normal with multiStrings.
uint8_t dataArray[] = {0, '\'', 0, '\'', ' ', '(', 'n', 'u', 'l', 'l', ')', ' ', 'v', 'a', 'l', 'u', 'e'};
String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + String(F(" from ")) + espnowNode.getMeshName() + espnowNode.getNodeID() + String(F("."));
Serial.println("\nTransmitting: " + espnowMessage);
String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.');
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response.
Serial.println("\nPerforming encrypted ESP-NOW transmissions.");
Serial.println(F("\nPerforming encrypted ESP-NOW transmissions."));
uint8_t targetBSSID[6] {0};
// We can create encrypted connections to individual nodes so that all ESP-NOW communication with the node will be encrypted.
if (espnowNode.constConnectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == ECS_CONNECTION_ESTABLISHED) {
if (espnowNode.constConnectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) {
// The WiFi scan will detect the AP MAC, but this will automatically be converted to the encrypted STA MAC by the framework.
String peerMac = TypeCast::macToString(targetBSSID);
Serial.println("Encrypted ESP-NOW connection with " + peerMac + " established!");
Serial.println(String(F("Encrypted ESP-NOW connection with ")) + peerMac + F(" established!"));
// Making a transmission now will cause messages to targetBSSID to be encrypted.
String espnowMessage = "This message is encrypted only when received by node " + peerMac;
Serial.println("\nTransmitting: " + espnowMessage);
String espnowMessage = String(F("This message is encrypted only when received by node ")) + peerMac;
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response.
@ -389,48 +384,48 @@ void loop() {
espnowNode.removeEncryptedConnection(targetBSSID);
// Note that the peer will still be encrypted, so although we can send unencrypted messages to the peer, we cannot read the encrypted responses it sends back.
espnowMessage = "This message is no longer encrypted when received by node " + peerMac;
Serial.println("\nTransmitting: " + espnowMessage);
espnowMessage = String(F("This message is no longer encrypted when received by node ")) + peerMac;
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response.
Serial.println("Cannot read the encrypted response...");
Serial.println(F("Cannot read the encrypted response..."));
// Let's re-add our stored connection so we can communicate properly with targetBSSID again!
espnowNode.addEncryptedConnection(serializedEncryptedConnection);
espnowMessage = "This message is once again encrypted when received by node " + peerMac;
Serial.println("\nTransmitting: " + espnowMessage);
espnowMessage = String(F("This message is once again encrypted when received by node ")) + peerMac;
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response.
Serial.println();
// If we want to remove the encrypted connection on both nodes, we can do it like this.
encrypted_connection_removal_outcome_t removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID);
if (removalOutcome == ECRO_REMOVAL_SUCCEEDED) {
Serial.println(peerMac + " is no longer encrypted!");
EncryptedConnectionRemovalOutcome removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID);
if (removalOutcome == EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED) {
Serial.println(peerMac + F(" is no longer encrypted!"));
espnowMessage = "This message is only received by node " + peerMac + ". Transmitting in this way will not change the transmission state of the sender.";
Serial.println("Transmitting: " + espnowMessage);
espnowMessage = String(F("This message is only received by node ")) + peerMac + F(". Transmitting in this way will not change the transmission state of the sender.");
Serial.println(String(F("Transmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, EspnowNetworkInfo(targetBSSID));
espnowDelay(100); // Wait for response.
Serial.println();
// Of course, we can also just create a temporary encrypted connection that will remove itself once its duration has passed.
if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == ECS_CONNECTION_ESTABLISHED) {
if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) {
espnowDelay(42);
uint32_t remainingDuration = 0;
EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowMessage = "Messages this node sends to " + peerMac + " will be encrypted for " + String(remainingDuration) + " ms more.";
Serial.println("\nTransmitting: " + espnowMessage);
espnowMessage = String(F("Messages this node sends to ")) + peerMac + F(" will be encrypted for ") + String(remainingDuration) + F(" ms more.");
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowDelay(remainingDuration + 100);
espnowMessage = "Due to encrypted connection expiration, this message is no longer encrypted when received by node " + peerMac;
Serial.println("\nTransmitting: " + espnowMessage);
espnowMessage = String(F("Due to encrypted connection expiration, this message is no longer encrypted when received by node ")) + peerMac;
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
espnowDelay(100); // Wait for response.
}
@ -438,24 +433,24 @@ void loop() {
// Or if we prefer we can just let the library automatically create brief encrypted connections which are long enough to transmit an encrypted message.
// Note that encrypted responses will not be received, unless there already was an encrypted connection established with the peer before attemptAutoEncryptingTransmission was called.
// This can be remedied via the requestPermanentConnections argument, though it must be noted that the maximum number of encrypted connections supported at a time is 6.
espnowMessage = "This message is always encrypted, regardless of receiver.";
Serial.println("\nTransmitting: " + espnowMessage);
espnowMessage = F("This message is always encrypted, regardless of receiver.");
Serial.println(String(F("\nTransmitting: ")) + espnowMessage);
espnowNode.attemptAutoEncryptingTransmission(espnowMessage);
espnowDelay(100); // Wait for response.
} else {
Serial.println("Ooops! Encrypted connection removal failed. Status: " + String(removalOutcome));
Serial.println(String(F("Ooops! Encrypted connection removal failed. Status: ")) + String(static_cast<int>(removalOutcome)));
}
// Finally, should you ever want to stop other parties from sending unencrypted messages to the node
// setAcceptsUnencryptedRequests(false);
// can be used for this. It applies to both encrypted connection requests and regular transmissions.
Serial.println("\n##############################################################################################");
Serial.println(F("\n##############################################################################################"));
}
// Our last request was sent to all nodes found, so time to create a new request.
espnowNode.setMessage(String(F("Hello world request #")) + String(++requestNumber) + String(F(" from "))
+ espnowNode.getMeshName() + espnowNode.getNodeID() + String(F(".")));
espnowNode.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ")
+ espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'));
}
Serial.println();

View File

@ -5,7 +5,7 @@
That way you will get instant confirmation of the mesh communication without checking the Serial Monitor.
*/
#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core.
#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core.
#include <ESP8266WiFi.h>
#include <TypeConversionFunctions.h>
@ -175,7 +175,7 @@ void loop() {
floodingMeshDelay(1);
// If you wish to transmit only to a single node, try using one of the following methods (requires the node to be within range and know the MAC of the recipient):
// Unencrypted: transmission_status_t floodingMesh.getEspnowMeshBackend().attemptTransmission(message, EspnowNetworkInfo(recipientMac));
// Unencrypted: TransmissionStatusType floodingMesh.getEspnowMeshBackend().attemptTransmission(message, EspnowNetworkInfo(recipientMac));
// Encrypted (slow): floodingMesh.getEspnowMeshBackend().attemptAutoEncryptingTransmission(message, EspnowNetworkInfo(recipientMac));
if (theOne) {

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

View File

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

View File

@ -35,8 +35,6 @@ namespace
size_t _ctMinDataLength = 0;
size_t _ctMaxDataLength = 1024;
bool _warningsEnabled = true;
br_hkdf_context _storedHkdfContext;
bool _hkdfContextStored = false;
@ -186,26 +184,44 @@ namespace
createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, 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
{
void setCtMinDataLength(const size_t ctMinDataLength)
{
assert(ctMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF);
assert(getCtMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF);
_ctMinDataLength = ctMinDataLength;
}
size_t ctMinDataLength() {return _ctMinDataLength;}
size_t getCtMinDataLength() {return _ctMinDataLength;}
void setCtMaxDataLength(const size_t ctMaxDataLength)
{
assert(ctMaxDataLength - ctMinDataLength() <= CT_MAX_DIFF);
assert(ctMaxDataLength - getCtMinDataLength() <= CT_MAX_DIFF);
_ctMaxDataLength = ctMaxDataLength;
}
size_t ctMaxDataLength() {return _ctMaxDataLength;}
void setWarningsEnabled(bool warningsEnabled) { _warningsEnabled = warningsEnabled; }
bool warningsEnabled() { return _warningsEnabled; }
size_t getCtMaxDataLength() {return _ctMaxDataLength;}
void setNonceGenerator(nonceGeneratorType nonceGenerator) { _nonceGenerator = nonceGenerator; }
nonceGeneratorType getNonceGenerator() { return _nonceGenerator; }
@ -216,22 +232,13 @@ namespace CryptoInterface
// resultArray must have size MD5_NATURAL_LENGTH or greater
void *md5Hash(const void *data, const size_t dataLength, void *resultArray)
{
if(warningsEnabled())
Serial.println(F("\nWARNING! The MD5 hash is broken in terms of attacker resistance.\n"
"Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n"
"Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n"));
br_md5_context context;
br_md5_init(&context);
br_md5_update(&context, data, dataLength);
br_md5_out(&context, resultArray);
return resultArray;
return md5HashHelper(data, dataLength, resultArray);
}
String md5Hash(const String &message)
{
uint8_t hash[MD5_NATURAL_LENGTH];
md5Hash(message.c_str(), message.length(), hash);
md5HashHelper(message.c_str(), message.length(), hash);
return TypeCast::uint8ArrayToHexString(hash, MD5_NATURAL_LENGTH);
}
@ -261,22 +268,13 @@ namespace CryptoInterface
// resultArray must have size SHA1_NATURAL_LENGTH or greater
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray)
{
if(warningsEnabled())
Serial.println(F("\nWARNING! The SHA-1 hash is broken in terms of attacker resistance.\n"
"Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.\n"
"Use CryptoInterface::setWarningsEnabled(false) to turn off this warning.\n"));
br_sha1_context context;
br_sha1_init(&context);
br_sha1_update(&context, data, dataLength);
br_sha1_out(&context, resultArray);
return resultArray;
return sha1HashHelper(data, dataLength, resultArray);
}
String sha1Hash(const String &message)
{
uint8_t hash[SHA1_NATURAL_LENGTH];
sha1Hash(message.c_str(), message.length(), hash);
sha1HashHelper(message.c_str(), message.length(), hash);
return TypeCast::uint8ArrayToHexString(hash, SHA1_NATURAL_LENGTH);
}

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,
* may leak, though; only the contents are protected."
*
* For messages much smaller than ctMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
* determined by the size of (ctMaxDataLength() - ctMinDataLength()).
* For messages much smaller than getCtMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
* determined by the size of (getCtMaxDataLength() - getCtMinDataLength()).
* Constant-time processing also sets limits on the data length.
*
* Making the fixed data length limits variable will generally defeat the purpose of using constant-time.
@ -78,34 +78,26 @@ namespace CryptoInterface
* It should not be changed once a constant time function has been used at least once.
* Otherwise the constant time will not be constant for the used functions.
*
* The difference ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
* The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMinDataLength(const size_t ctMinDataLength);
/**
* 0 by default.
*/
size_t ctMinDataLength();
size_t getCtMinDataLength();
/**
* This function allows for fine-tuning of the specifications for the constant time calculations.
* It should not be changed once a constant time function has been used at least once.
* Otherwise the constant time will not be constant for the used functions.
*
* The difference ctMaxDataLength() - ctMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
* The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMaxDataLength(const size_t ctMaxDataLength);
/**
* 1024 by default.
*/
size_t ctMaxDataLength();
/**
* Turn on or off warning Serial prints from the CryptoInterface functions.
*
* @param warningsEnabled If true, warnings will be printed to Serial.
*/
void setWarningsEnabled(bool warningsEnabled);
bool warningsEnabled();
size_t getCtMaxDataLength();
/**
* Set the nonce generator used by the CryptoInterface functions.
@ -119,6 +111,9 @@ namespace CryptoInterface
// #################### MD5 ####################
/**
* WARNING! The MD5 hash is broken in terms of attacker resistance.
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
*
* Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and stored in resultArray.
* Uses the BearSSL cryptographic library.
*
@ -128,9 +123,12 @@ namespace CryptoInterface
*
* @return A pointer to resultArray.
*/
void *md5Hash(const void *data, const size_t dataLength, void *resultArray);
void *md5Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
/**
* WARNING! The MD5 hash is broken in terms of attacker resistance.
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
*
* Create a MD5 hash of the data. The result will be MD5_NATURAL_LENGTH bytes long and returned as a String in HEX format.
* Uses the BearSSL cryptographic library.
*
@ -138,7 +136,7 @@ namespace CryptoInterface
*
* @return A String with the generated hash in HEX format.
*/
String md5Hash(const String &message);
String md5Hash(const String &message) __attribute__((deprecated));
/**
* Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
@ -176,7 +174,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -193,7 +191,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to MD5_NATURAL_LENGTH.
@ -206,6 +204,9 @@ namespace CryptoInterface
// #################### SHA-1 ####################
/**
* WARNING! The SHA-1 hash is broken in terms of attacker resistance.
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
*
* Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and stored in resultArray.
* Uses the BearSSL cryptographic library.
*
@ -215,9 +216,12 @@ namespace CryptoInterface
*
* @return A pointer to resultArray.
*/
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray);
void *sha1Hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
/**
* WARNING! The SHA-1 hash is broken in terms of attacker resistance.
* Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
*
* Create a SHA1 hash of the data. The result will be SHA1_NATURAL_LENGTH bytes long and returned as a String in HEX format.
* Uses the BearSSL cryptographic library.
*
@ -225,7 +229,7 @@ namespace CryptoInterface
*
* @return A String with the generated hash in HEX format.
*/
String sha1Hash(const String &message);
String sha1Hash(const String &message) __attribute__((deprecated));
/**
* Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
@ -263,7 +267,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -280,7 +284,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA1_NATURAL_LENGTH.
@ -350,7 +354,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -367,7 +371,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA224_NATURAL_LENGTH.
@ -437,7 +441,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -454,7 +458,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA256_NATURAL_LENGTH.
@ -524,7 +528,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -541,7 +545,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA384_NATURAL_LENGTH.
@ -611,7 +615,7 @@ namespace CryptoInterface
* Uses the BearSSL cryptographic library.
*
* @param data The data array from which to create the HMAC.
* @param dataLength The length of the data array in bytes. Valid values are in the range [ctMinDataLength(), ctMaxDataLength()].
* @param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param resultArray The array wherein to store the resulting HMAC.
@ -628,7 +632,7 @@ namespace CryptoInterface
* Constant-time version.
* Uses the BearSSL cryptographic library.
*
* @param message The string from which to create the HMAC. Must have a length in the range [ctMinDataLength(), ctMaxDataLength()].
* @param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
* @param hashKey The hash key to use when creating the HMAC.
* @param hashKeyLength The length of the hash key in bytes.
* @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to SHA512_NATURAL_LENGTH.

View File

@ -54,7 +54,7 @@ namespace TypeCast = MeshTypeConversionFunctions;
const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress();
String ESP8266WiFiMesh::lastSSID = "";
String ESP8266WiFiMesh::lastSSID;
bool ESP8266WiFiMesh::staticIPActivated = false;
// IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server.
@ -88,9 +88,9 @@ ESP8266WiFiMesh::ESP8266WiFiMesh(ESP8266WiFiMesh::requestHandlerType requestHand
void ESP8266WiFiMesh::updateNetworkNames(const String &newMeshName, const String &newNodeID)
{
if(newMeshName != "")
if(!newMeshName.isEmpty())
_meshName = newMeshName;
if(newNodeID != "")
if(!newNodeID.isEmpty())
_nodeID = newNodeID;
String newSSID = _meshName + _nodeID;
@ -469,7 +469,7 @@ void ESP8266WiFiMesh::initiateConnectionToAP(const String &targetSSID, int targe
*/
transmission_status_t ESP8266WiFiMesh::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID)
{
if(staticIPActivated && lastSSID != "" && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact.
if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact.
{
#if LWIP_VERSION_MAJOR >= 2
// Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches.
@ -567,12 +567,12 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding
WiFi.disconnect();
yield();
String currentSSID = "";
String currentSSID;
int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT;
uint8_t *currentBSSID = NULL;
// If an SSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change.
if(currentNetwork.SSID != "")
if(!currentNetwork.SSID.isEmpty())
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -90,32 +90,34 @@
#include "EspnowNetworkInfo.h"
#include "CryptoInterface.h"
typedef enum
namespace Espnow
{
ECT_NO_CONNECTION = 0,
ECT_TEMPORARY_CONNECTION = 1,
ECT_PERMANENT_CONNECTION = 2
} espnow_connection_type_t;
enum class ConnectionType
{
NO_CONNECTION = 0,
TEMPORARY_CONNECTION = 1,
PERMANENT_CONNECTION = 2
};
// A value greater than 0 means that an encrypted connection has been established.
typedef enum
enum class EncryptedConnectionStatus
{
ECS_MAX_CONNECTIONS_REACHED_SELF = -3,
ECS_REQUEST_TRANSMISSION_FAILED = -2,
ECS_MAX_CONNECTIONS_REACHED_PEER = -1,
ECS_API_CALL_FAILED = 0,
ECS_CONNECTION_ESTABLISHED = 1,
ECS_SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6.
} encrypted_connection_status_t;
MAX_CONNECTIONS_REACHED_SELF = -3,
REQUEST_TRANSMISSION_FAILED = -2,
MAX_CONNECTIONS_REACHED_PEER = -1,
API_CALL_FAILED = 0,
CONNECTION_ESTABLISHED = 1,
SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6.
};
typedef enum
enum class EncryptedConnectionRemovalOutcome
{
ECRO_REMOVAL_REQUEST_FAILED = -1,
ECRO_REMOVAL_FAILED = 0,
ECRO_REMOVAL_SUCCEEDED = 1,
ECRO_REMOVAL_SCHEDULED = 2
} encrypted_connection_removal_outcome_t;
REMOVAL_REQUEST_FAILED = -1,
REMOVAL_FAILED = 0,
REMOVAL_SUCCEEDED = 1,
REMOVAL_SCHEDULED = 2
};
}
/**
* An alternative to standard delay(). Will continuously call performEspnowMaintenance() during the waiting time, so that the ESP-NOW node remains responsive.
@ -130,12 +132,14 @@ void espnowDelay(uint32_t durationMs);
class RequestData;
using namespace Espnow; // TODO: Remove
class EspnowMeshBackend : public MeshBackendBase {
protected:
typedef std::function<bool(String &, EspnowMeshBackend &)> broadcastFilterType;
typedef std::function<bool(const String &, const uint8_t *, uint32_t, EspnowMeshBackend &)> responseTransmittedHookType;
using broadcastFilterType = std::function<bool(String &, EspnowMeshBackend &)>;
using responseTransmittedHookType = std::function<bool(const String &, const uint8_t *, uint32_t, EspnowMeshBackend &)>;
public:
@ -145,7 +149,7 @@ public:
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which
* is the request string received from another node and returns the string to send back.
* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which
* is the response string received from another node. Returns a transmission status code as a transmission_status_t.
* is the response string received from another node. Returns a transmission status code as a TransmissionStatusType.
* @param networkFilter The callback handler for deciding which WiFi networks to connect to.
* @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept.
* @param meshPassword The WiFi password for the mesh network.
@ -155,7 +159,7 @@ public:
* @param ssidSuffix The suffix (last part) of the node SSID.
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
@ -173,7 +177,7 @@ public:
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which
* is the request string received from another node and returns the string to send back.
* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which
* is the response string received from another node. Returns a transmission status code as a transmission_status_t.
* is the response string received from another node. Returns a transmission status code as a TransmissionStatusType.
* @param networkFilter The callback handler for deciding which WiFi networks to connect to.
* @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept.
* @param meshPassword The WiFi password for the mesh network.
@ -183,7 +187,7 @@ public:
* @param ssidSuffix The suffix (last part) of the node SSID.
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
@ -222,7 +226,7 @@ public:
static std::vector<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.
*/
static bool latestTransmissionSuccessful();
@ -285,7 +289,7 @@ public:
*
* @param recipientInfo The recipient information.
*/
transmission_status_t attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
TransmissionStatusType attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
/*
* Will ensure that an encrypted connection exists to each target node before sending the message,
@ -317,7 +321,7 @@ public:
* Transmit message to a single recipient without changing the local transmission state (apart from encrypted connections).
* Will not change connectionQueue, latestTransmissionOutcomes or stored message.
*/
transmission_status_t attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection = false);
TransmissionStatusType attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection = false);
/**
* Send a message simultaneously to all nearby nodes which have ESP-NOW activated.
@ -736,32 +740,32 @@ public:
// Updates connection with current stored encrypted connection key.
// At least one of the leftmost 32 bits in each of the session keys should be 1, since the key otherwise indicates the connection is unencrypted.
encrypted_connection_status_t addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey);
EncryptedConnectionStatus addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey);
// Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized.
// These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection.
// @param ignoreDuration Ignores any stored duration serializedConnectionState, guaranteeing that the created connection will be permanent. Returns: ECS_REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
encrypted_connection_status_t addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration = false);
// @param ignoreDuration Ignores any stored duration serializedConnectionState, guaranteeing that the created connection will be permanent. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
EncryptedConnectionStatus addEncryptedConnection(const String &serializedConnectionState, bool ignoreDuration = false);
// Adds a new temporary encrypted connection, or changes the duration of an existing temporary connection (only updates keys, not duration, for existing permanent connections).
// As with all these methods, changes will only take effect once the requester proves it has the ability to decrypt the session key.
// At least one of the leftmost 32 bits in each of the session keys should be 1, since the key otherwise indicates the connection is unencrypted.
encrypted_connection_status_t addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration);
EncryptedConnectionStatus addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, uint64_t peerSessionKey, uint64_t ownSessionKey, uint32_t duration);
// Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized.
// These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection.
// Uses duration argument instead of any stored duration in serializedConnectionState. Returns: ECS_REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
encrypted_connection_status_t addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration);
// Uses duration argument instead of any stored duration in serializedConnectionState. Returns: EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates malformed serializedConnectionState.
EncryptedConnectionStatus addTemporaryEncryptedConnection(const String &serializedConnectionState, uint32_t duration);
// If an encrypted connection to peerMac already exists, only connection duration is updated. All other settings are kept as is. Use removeEncryptedConnection/requestEncryptedConnectionRemoval first if encryption keys should be updated.
// Makes sure both nodes have an encrypted connection to each other that's permanent.
encrypted_connection_status_t requestEncryptedConnection(uint8_t *peerMac);
EncryptedConnectionStatus requestEncryptedConnection(uint8_t *peerMac);
// Makes sure both nodes have an encrypted connection to each other that's either permanent or has the duration specified.
encrypted_connection_status_t requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs);
EncryptedConnectionStatus requestTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t durationMs);
// Makes sure both nodes have an encrypted connection to each other that's either permanent or has at least the duration specified.
// Note that if a temporary encrypted connection already exists to a target node, this method will slightly extend the connection duration
// depending on the time it takes to verify the connection to the node.
encrypted_connection_status_t requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs);
static encrypted_connection_removal_outcome_t removeEncryptedConnection(uint8_t *peerMac);
encrypted_connection_removal_outcome_t requestEncryptedConnectionRemoval(uint8_t *peerMac);
EncryptedConnectionStatus requestFlexibleTemporaryEncryptedConnection(uint8_t *peerMac, uint32_t minDurationMs);
static EncryptedConnectionRemovalOutcome removeEncryptedConnection(uint8_t *peerMac);
EncryptedConnectionRemovalOutcome requestEncryptedConnectionRemoval(uint8_t *peerMac);
/**
* Set whether this EspnowMeshBackend instance will accept ESP-NOW requests from unencrypted connections or not, when acting as EspnowRequestManager.
@ -824,24 +828,24 @@ public:
* @param peerMac The node MAC for which to get information. Both MAC for AP interface and MAC for STA interface can be used (and will yield the same result).
* Use the getEncryptedMac method or the indexed based getConnectionInfo if there is a need to find the actual encrypted interface.
* @param remainingDuration An optional pointer to a uint32_t variable.
* If supplied and the connection type is ECT_TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection.
* If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection.
* Otherwise the variable value is not modified.
* @return The espnow_connection_type_t of the connection with peerMac.
* @return The ConnectionType of the connection with peerMac.
*/
static espnow_connection_type_t getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr);
static ConnectionType getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr);
/**
* Get information about any current ESP-NOW connection with another node.
*
* @param connectionIndex The connection index of the node for which to get information. Valid values are limited by numberOfEncryptedConnections().
* @param remainingDuration An optional pointer to a uint32_t variable.
* If supplied and the connection type is ECT_TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection.
* If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection.
* Otherwise the variable value is not modified.
* @param peerMac An optional pointer to an uint8_t array with at least size 6. It will be filled with the MAC of the encrypted peer interface if an encrypted connection exists.
* Otherwise the array is not modified.
* @return The espnow_connection_type_t of the connection given by connectionIndex.
* @return The ConnectionType of the connection given by connectionIndex.
*/
static espnow_connection_type_t getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr);
static ConnectionType getConnectionInfo(uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr);
/**
* @return The proportion of ESP-NOW requests made by this node that have failed, since power on or latest reset.
@ -858,7 +862,7 @@ protected:
static std::vector<EspnowNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
typedef std::vector<EncryptedConnectionLog>::iterator connectionLogIterator;
using connectionLogIterator = std::vector<EncryptedConnectionLog>::iterator;
static connectionLogIterator connectionLogEndIterator();
static const uint8_t broadcastMac[6];
@ -866,7 +870,7 @@ protected:
bool activateEspnow();
static bool encryptedConnectionEstablished(encrypted_connection_status_t connectionStatus);
static bool encryptedConnectionEstablished(EncryptedConnectionStatus connectionStatus);
/*
* Note that ESP-NOW is not perfect and in rare cases messages may be dropped.
@ -906,8 +910,8 @@ protected:
// Consider using getScheduledResponseRecipient and similar methods for this preparation.
// Should only be used when there is no transmissions in progress. In practice when _espnowTransmissionMutex is free.
// @param resultingIterator Will be set to the iterator position after the removed element, if an element to remove was found. Otherwise no change will occur.
static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator = nullptr);
static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector<EncryptedConnectionLog>::iterator *resultingIterator);
static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator = nullptr);
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.
@ -935,17 +939,17 @@ protected:
/**
* Will be true if a transmission initiated by a public method is in progress.
*/
static bool _espnowTransmissionMutex;
static std::shared_ptr<bool> _espnowTransmissionMutex;
/**
* 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.
*/
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.
@ -955,8 +959,8 @@ protected:
static bool transmissionInProgress();
enum class macAndType_td : uint64_t {};
typedef uint64_t messageID_td;
typedef uint64_t peerMac_td;
using messageID_td = uint64_t;
using peerMac_td = uint64_t;
static macAndType_td createMacAndTypeValue(uint64_t uint64Mac, char messageType);
static uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue);
@ -985,19 +989,19 @@ protected:
* @return The transmission status for the transmission.
*/
// Send a message to the node having targetBSSID as mac, changing targetBSSID to the mac of the encrypted connection if it exists and ensuring such an encrypted connection is synchronized.
static transmission_status_t espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance = nullptr);
static TransmissionStatusType espnowSendToNode(const String &message, const uint8_t *targetBSSID, char messageType, EspnowMeshBackend *espnowInstance = nullptr);
// Send a message using exactly the arguments given, without consideration for any encrypted connections.
static transmission_status_t espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance = nullptr);
static TransmissionStatusType espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, char messageType, uint64_t messageID, EspnowMeshBackend *espnowInstance = nullptr);
transmission_status_t sendRequest(const String &message, const uint8_t *targetBSSID);
transmission_status_t sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID);
TransmissionStatusType sendRequest(const String &message, const uint8_t *targetBSSID);
TransmissionStatusType sendResponse(const String &message, uint64_t requestID, const uint8_t *targetBSSID);
private:
EspnowMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, broadcastFilterType broadcastFilter,
const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel);
typedef std::function<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 flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker);
@ -1055,14 +1059,14 @@ private:
static EncryptedConnectionLog *getEncryptedConnection(const uint8_t *peerMac);
static EncryptedConnectionLog *getTemporaryEncryptedConnection(const uint8_t *peerMac);
//@return iterator to connection in connectionVector, or connectionVector.end() if element not found
//@return iterator to connection in connectionContainer, or connectionContainer.end() if element not found
template <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);
// @return true if an encrypted connection to peerMac is found and the found connection is temporary. Only changes iterator if true is returned.
static bool getTemporaryEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator);
static espnow_connection_type_t getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr);
static ConnectionType getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr);
// Should only be used when there is no transmissions in progress, so it is safe to remove encrypted connections. In practice when _espnowTransmissionMutex is free.
// @param scheduledRemovalOnly If true, only deletes encrypted connections where removalScheduled() is true. This means only connections which have been requested for removal will be deleted,
@ -1072,6 +1076,9 @@ private:
template <typename T, typename U>
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);
template <typename T>
@ -1082,7 +1089,6 @@ private:
static uint32_t _encryptionRequestTimeoutMs;
static uint32_t _timeOfLastLogClear;
static uint32_t _criticalHeapLevel;
static uint32_t _criticalHeapLevelBuffer;
@ -1096,8 +1102,7 @@ private:
static String _ongoingPeerRequestNonce;
static uint8_t _ongoingPeerRequestMac[6];
static EspnowMeshBackend *_ongoingPeerRequester;
static encrypted_connection_status_t _ongoingPeerRequestResult;
static uint32_t _ongoingPeerRequestEncryptionStart;
static EncryptedConnectionStatus _ongoingPeerRequestResult;
static bool _reciprocalPeerRequestConfirmation;
template <typename T>
@ -1119,7 +1124,7 @@ private:
uint8_t _senderAPMac[6] = {0};
bool _receivedEncryptedTransmission = false;
static bool _espnowSendToNodeMutex;
static std::shared_ptr<bool> _espnowSendToNodeMutex;
static uint8_t _transmissionTargetBSSID[6];
static void storeSentRequest(const uint64_t targetBSSID, const uint64_t messageID, const RequestData &requestData);
@ -1148,9 +1153,9 @@ private:
* @param encryptionRequestBuilder A function which is responsible for constructing the request message to send.
* Called twice when the request is successful. First to build the initial request message and then to build the connection verification message.
* The request message should typically be of the form: JsonTranslator::createEncryptionRequestIntro() + JsonTranslator::createEncryptionRequestEnding().
* @return The ultimate status of the requested encrypted connection, as encrypted_connection_status_t.
* @return The ultimate status of the requested encrypted connection, as EncryptedConnectionStatus.
*/
encrypted_connection_status_t requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder);
EncryptedConnectionStatus requestEncryptedConnectionKernel(uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder);
/**
* Generate a new message ID to be used when making a data transmission. The generated ID will be different depending on whether an encrypted connection exists or not.
@ -1170,12 +1175,12 @@ private:
static uint64_t createSessionKey();
void prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels);
transmission_status_t initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
transmission_status_t initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID);
TransmissionStatusType initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
TransmissionStatusType initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID);
void printTransmissionStatistics();
encrypted_connection_status_t initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection);
transmission_status_t initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, encrypted_connection_status_t connectionStatus);
EncryptedConnectionStatus initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection);
TransmissionStatusType initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, EncryptedConnectionStatus connectionStatus);
void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, bool requestPermanentConnection);
// Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter

View File

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

View File

@ -24,36 +24,101 @@
#include "ExpiringTimeTracker.h"
ExpiringTimeTracker::ExpiringTimeTracker(uint32_t duration, uint32_t creationTimeMs) :
TimeTracker(creationTimeMs), _duration(duration)
{ }
ExpiringTimeTracker::ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs) :
timeoutTemplate(0)
{
setDuration(duration);
_start = creationTimeMs;
}
ExpiringTimeTracker::ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs) :
timeoutTemplate(0)
{
setDuration(durationCalculator);
_start = creationTimeMs;
}
uint32_t ExpiringTimeTracker::duration() const
{
return _duration;
if(useCalculator)
return _durationCalculator();
return getTimeout();
}
void ExpiringTimeTracker::setRemainingDuration(uint32_t remainingDuration)
IRAM_ATTR // called from ISR
void ExpiringTimeTracker::setTimeout(const uint32_t newUserTimeout)
{
_duration = timeSinceCreation() + remainingDuration;
_timeout = newUserTimeout;
_neverExpires = (newUserTimeout > timeMax()); // newUserTimeout < 0 is always false for uint32_t
}
void ExpiringTimeTracker::setDuration(const uint32_t duration)
{
setTimeout(duration);
useCalculator = false;
}
void ExpiringTimeTracker::setDuration(const calculatorType durationCalculator)
{
_durationCalculator = durationCalculator;
useCalculator = true;
}
void ExpiringTimeTracker::setRemainingDuration(const uint32_t remainingDuration)
{
setDuration(elapsedTime() + remainingDuration);
}
void ExpiringTimeTracker::setRemainingDuration(const calculatorType remainingDurationCalculator)
{
uint32_t currentElapsedTime = elapsedTime();
setDuration([remainingDurationCalculator, currentElapsedTime](){ return currentElapsedTime + remainingDurationCalculator(); });
}
uint32_t ExpiringTimeTracker::remainingDuration() const
{
uint32_t remainingDuration = duration() - timeSinceCreation();
uint32_t remainingDuration = 0;
if(expired())
if(!expired()) // If expired, overflow will probably occur for remainingDuration calculation.
{
// Overflow probably occured for remainingDuration calculation.
return 0;
}
else
{
return remainingDuration;
remainingDuration = duration() - elapsedTime();
}
return remainingDuration;
}
uint32_t ExpiringTimeTracker::elapsedTime() const
{
return millis() - _start;
}
bool ExpiringTimeTracker::expired() const
{
return timeSinceCreation() >= duration();
if(useCalculator)
return elapsedTime() >= duration();
return expiredOneShot();
}
void ExpiringTimeTracker::reset()
{
timeoutTemplate::reset();
}
void ExpiringTimeTracker::reset(const uint32_t newDuration)
{
setDuration(newDuration);
ExpiringTimeTracker::reset();
}
void ExpiringTimeTracker::reset(const calculatorType newDurationCalculator)
{
setDuration(newDurationCalculator);
ExpiringTimeTracker::reset();
}
ExpiringTimeTracker::operator bool() const
{
return ExpiringTimeTracker::expired();
}

View File

@ -25,24 +25,54 @@
#ifndef __EXPIRINGTIMETRACKER_H__
#define __EXPIRINGTIMETRACKER_H__
#include "TimeTracker.h"
#include <Arduino.h>
#include <PolledTimeout.h>
class ExpiringTimeTracker : public TimeTracker {
class ExpiringTimeTracker : private esp8266::polledTimeout::oneShotMs {
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;
void setRemainingDuration(uint32_t remainingDuration);
void setDuration(const uint32_t duration);
void setDuration(const calculatorType durationCalculator);
uint32_t remainingDuration() const;
/**
* Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset.
* Note that reset() will use this new duration, including the saved elapsedTime().
*/
void setRemainingDuration(const uint32_t remainingDuration);
/**
* Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset.
* Note that reset() will use this new duration, including the saved elapsedTime().
*/
void setRemainingDuration(const calculatorType remainingDurationCalculator);
/**
* Get the time since the ExpiringTimeTracker instance creation or the last reset(), whichever is more recent.
*/
uint32_t elapsedTime() const;
bool expired() const;
void reset();
void reset(const uint32_t newDuration);
void reset(const calculatorType newDurationCalculator);
explicit operator bool() const;
private:
uint32_t _duration;
bool useCalculator = false;
calculatorType _durationCalculator;
void setTimeout(const uint32_t newUserTimeout);
};
#endif

View File

@ -40,13 +40,16 @@ char FloodingMesh::_metadataDelimiter = 23;
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);
FloodingMesh::performMeshMaintenance();
}
while(!timeout);
}
FloodingMesh::FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::espnowEncryptedConnectionKeyLength],
@ -428,9 +431,9 @@ String FloodingMesh::_defaultRequestHandler(const String &request, MeshBackendBa
* @param meshInstance The MeshBackendBase instance that called the function.
* @return The status code resulting from the response, as an int
*/
transmission_status_t FloodingMesh::_defaultResponseHandler(const String &response, MeshBackendBase &meshInstance)
TransmissionStatusType FloodingMesh::_defaultResponseHandler(const String &response, MeshBackendBase &meshInstance)
{
transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE;
TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE;
getEspnowMeshBackend().warningPrint(String(F("WARNING! Response to FloodingMesh broadcast received, but none is expected!")));
@ -503,26 +506,22 @@ bool FloodingMesh::_defaultBroadcastFilter(String &firstTransmission, EspnowMesh
{
return false; // Broadcast is for another mesh network
}
else
int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1);
if(messageIDEndIndex == -1)
return false; // metadataDelimiter not found
uint64_t messageID = TypeCast::stringToUint64(firstTransmission.substring(metadataEndIndex + 1, messageIDEndIndex));
if(insertPreliminaryMessageID(messageID))
{
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))
{
// 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
}
// Add broadcast identifier to stored message and mark as accepted broadcast.
firstTransmission = String(metadataDelimiter()) + firstTransmission;
return true;
}
return false; // Broadcast has already been received the maximum number of times
}
/**

View File

@ -27,7 +27,6 @@
#include "EspnowMeshBackend.h"
#include <set>
#include <unordered_map>
#include <queue>
/**
@ -45,8 +44,8 @@ class FloodingMesh {
protected:
typedef std::function<bool(String &, FloodingMesh &)> messageHandlerType;
typedef std::unordered_map<uint64_t, uint8_t>::iterator messageQueueElementType;
using messageHandlerType = std::function<bool(String &, FloodingMesh &)>;
using messageQueueElementType = std::map<uint64_t, uint8_t>::iterator;
public:
@ -61,7 +60,7 @@ public:
* @param ssidSuffix The suffix (last part) of the node SSID.
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
@ -83,7 +82,7 @@ public:
* @param ssidSuffix The suffix (last part) of the node SSID.
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
@ -321,14 +320,14 @@ private:
uint8_t _originMac[6] = {0};
std::unordered_map<uint64_t, uint8_t> _messageIDs = {};
std::map<uint64_t, uint8_t> _messageIDs = {};
std::queue<messageQueueElementType> _messageIdOrder = {};
std::list<std::pair<String, bool>> _forwardingBacklog = {};
String _macIgnoreList;
String _defaultRequestHandler(const String &request, MeshBackendBase &meshInstance);
transmission_status_t _defaultResponseHandler(const String &response, MeshBackendBase &meshInstance);
TransmissionStatusType _defaultResponseHandler(const String &response, MeshBackendBase &meshInstance);
void _defaultNetworkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance);
bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance);

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;
bool MeshBackendBase::_scanMutex = false;
std::shared_ptr<bool> MeshBackendBase::_scanMutex = std::make_shared<bool>(false);
bool MeshBackendBase::_printWarnings = true;
MeshBackendBase::MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, mesh_backend_t classType)
MeshBackendBase::MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, MeshBackendType classType)
{
setRequestHandler(requestHandler);
setResponseHandler(responseHandler);
@ -43,12 +43,12 @@ MeshBackendBase::~MeshBackendBase()
deactivateControlledAP();
}
void MeshBackendBase::setClassType(mesh_backend_t classType)
void MeshBackendBase::setClassType(MeshBackendType classType)
{
_classType = classType;
}
mesh_backend_t MeshBackendBase::getClassType() {return _classType;}
MeshBackendType MeshBackendBase::getClassType() {return _classType;}
void MeshBackendBase::activateAP()
{
@ -115,7 +115,9 @@ bool MeshBackendBase::isAPController()
void MeshBackendBase::setWiFiChannel(uint8 newWiFiChannel)
{
assert(1 <= newWiFiChannel && newWiFiChannel <= 13);
wifi_country_t wifiCountry;
wifi_get_country(&wifiCountry); // Note: Should return 0 on success and -1 on failure, but always seems to return 1. Possibly broken API. Channels 1 to 13 are the default limits.
assert(wifiCountry.schan <= newWiFiChannel && newWiFiChannel <= wifiCountry.schan + wifiCountry.nchan - 1);
_meshWiFiChannel = newWiFiChannel;
@ -248,10 +250,10 @@ bool MeshBackendBase::latestTransmissionSuccessfulBase(const std::vector<Transmi
{
if(latestTransmissionOutcomes.empty())
return false;
else
for(const TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes)
if(transmissionOutcome.transmissionStatus() != TS_TRANSMISSION_COMPLETE)
return false;
for(const TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes)
if(transmissionOutcome.transmissionStatus() != TransmissionStatusType::TRANSMISSION_COMPLETE)
return false;
return true;
}

View File

@ -23,24 +23,24 @@
#include "TransmissionOutcome.h"
#include "NetworkInfoBase.h"
typedef enum
enum class MeshBackendType
{
MB_TCP_IP = 0,
MB_ESP_NOW = 1
} mesh_backend_t;
TCP_IP = 0,
ESP_NOW = 1
};
class MeshBackendBase {
protected:
typedef std::function<String(const String &, MeshBackendBase &)> requestHandlerType;
typedef std::function<transmission_status_t(const String &, MeshBackendBase &)> responseHandlerType;
typedef std::function<void(int, MeshBackendBase &)> networkFilterType;
typedef std::function<bool(MeshBackendBase &)> transmissionOutcomesUpdateHookType;
using requestHandlerType = std::function<String(const String &, MeshBackendBase &)> ;
using responseHandlerType = std::function<TransmissionStatusType(const String &, MeshBackendBase &)>;
using networkFilterType = std::function<void(int, MeshBackendBase &)>;
using transmissionOutcomesUpdateHookType = std::function<bool(MeshBackendBase &)>;
public:
MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, mesh_backend_t classType);
MeshBackendBase(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, MeshBackendType classType);
virtual ~MeshBackendBase();
@ -109,13 +109,13 @@ public:
* Will also change the WiFi channel for the active AP (via an AP restart)
* if this MeshBackendBase instance is the current AP controller and it is possible to change channel.
*
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several MeshBackendBase instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one MeshBackendBase instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
* make it impossible for other stations to detect the APs whose WiFi channels have changed.
*
* @param newWiFiChannel The WiFi channel to change to. Valid values are integers from 1 to 13.
* @param newWiFiChannel The WiFi channel to change to. Valid values are determined by wifi_get_country, usually integers from 1 to 11 or 1 to 13.
*
*/
void setWiFiChannel(uint8 newWiFiChannel);
@ -284,14 +284,14 @@ public:
*/
static void warningPrint(const String &stringToPrint, bool newline = true);
mesh_backend_t getClassType();
MeshBackendType getClassType();
protected:
/**
* @param latestTransmissionOutcomes The transmission outcomes vector to check.
*
* @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise.
* @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise.
*/
static bool latestTransmissionSuccessfulBase(const std::vector<TransmissionOutcome> &latestTransmissionOutcomes);
@ -310,13 +310,13 @@ protected:
*/
virtual void deactivateAPHook();
void setClassType(mesh_backend_t classType);
void setClassType(MeshBackendType classType);
static bool _scanMutex;
static std::shared_ptr<bool> _scanMutex;
private:
mesh_backend_t _classType;
MeshBackendType _classType;
static MeshBackendBase *apController;

View File

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

View File

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

View File

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

View File

@ -26,6 +26,7 @@
#define __MUTEXTRACKER_H__
#include <functional>
#include <memory>
/**
* A SLIM (Scope LImited Manager)/Scope-Bound Resource Management/RAII class to manage the state of a mutex.
@ -39,23 +40,23 @@ class MutexTracker
* Set to false by default.
* captureBan can be managed by MutexTracker like any other mutex.
*/
static bool &captureBan();
static std::shared_ptr<bool> captureBan();
/**
* 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.
*
* @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();
bool mutexCaptured();
bool mutexCaptured() const;
/**
* Set the mutex free to roam the binary plains, giving new MutexTrackers a chance to capture it.
@ -64,17 +65,20 @@ class MutexTracker
private:
static bool _captureBan;
static std::shared_ptr<bool> _captureBan;
bool *_capturedMutex = nullptr;
std::shared_ptr<bool> _capturedMutex;
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.
*
* @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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,8 +32,8 @@ namespace
const IPAddress TcpIpMeshBackend::emptyIP;
bool TcpIpMeshBackend::_tcpIpTransmissionMutex = false;
bool TcpIpMeshBackend::_tcpIpConnectionQueueMutex = false;
std::shared_ptr<bool> TcpIpMeshBackend::_tcpIpTransmissionMutex = std::make_shared<bool>(false);
std::shared_ptr<bool> TcpIpMeshBackend::_tcpIpConnectionQueueMutex = std::make_shared<bool>(false);
String TcpIpMeshBackend::lastSSID;
bool TcpIpMeshBackend::staticIPActivated = false;
@ -51,7 +51,7 @@ std::vector<TransmissionOutcome> TcpIpMeshBackend::_latestTransmissionOutcomes =
TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler,
networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix,
const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort)
: MeshBackendBase(requestHandler, responseHandler, networkFilter, MB_TCP_IP), _server(serverPort)
: MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::TCP_IP), _server(serverPort)
{
setSSID(ssidPrefix, emptyString, ssidSuffix);
setMeshPassword(meshPassword);
@ -111,7 +111,7 @@ void TcpIpMeshBackend::deactivateAPHook()
_server.stop();
}
bool TcpIpMeshBackend::transmissionInProgress(){return _tcpIpTransmissionMutex;}
bool TcpIpMeshBackend::transmissionInProgress(){return *_tcpIpTransmissionMutex;}
void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;}
String TcpIpMeshBackend::getTemporaryMessage() {return _temporaryMessage;}
@ -180,12 +180,12 @@ void TcpIpMeshBackend::setMaxAPStations(uint8_t maxAPStations)
bool TcpIpMeshBackend::getMaxAPStations() {return _maxAPStations;}
void TcpIpMeshBackend::setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs)
void TcpIpMeshBackend::setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs)
{
_connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
}
int32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;}
uint32_t TcpIpMeshBackend::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;}
void TcpIpMeshBackend::setStationModeTimeout(int stationModeTimeoutMs)
{
@ -220,12 +220,11 @@ void TcpIpMeshBackend::fullStop(WiFiClient &currClient)
*/
bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait)
{
uint32_t connectionStartTime = millis();
uint32_t waitingTime = millis() - connectionStartTime;
while(currClient.connected() && !currClient.available() && waitingTime < maxWait)
ExpiringTimeTracker timeout(maxWait);
while(currClient.connected() && !currClient.available() && !timeout)
{
delay(1);
waitingTime = millis() - connectionStartTime;
}
/* Return false if the client isn't ready to communicate */
@ -246,7 +245,7 @@ bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, uint32_
* @return A status code based on the outcome of the exchange.
*
*/
transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
TransmissionStatusType TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
{
verboseModePrint(String(F("Transmitting")));
@ -256,13 +255,13 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
if (!waitForClientTransmission(currClient, _stationModeTimeoutMs))
{
fullStop(currClient);
return TS_CONNECTION_FAILED;
return TransmissionStatusType::CONNECTION_FAILED;
}
if (!currClient.available())
{
verboseModePrint(F("No response!"));
return TS_TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks.
return TransmissionStatusType::TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks.
}
String response = currClient.readStringUntil('\r');
@ -278,7 +277,7 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
*
* @return A status code based on the outcome of the data transfer attempt.
*/
transmission_status_t TcpIpMeshBackend::attemptDataTransfer()
TransmissionStatusType TcpIpMeshBackend::attemptDataTransfer()
{
// Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations.
// We cannot send data to the AP in STA_AP mode though, that requires STA mode.
@ -286,7 +285,7 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransfer()
WiFiMode_t storedWiFiMode = WiFi.getMode();
WiFi.mode(WIFI_STA);
delay(1);
transmission_status_t transmissionOutcome = attemptDataTransferKernel();
TransmissionStatusType transmissionOutcome = attemptDataTransferKernel();
WiFi.mode(storedWiFiMode);
delay(1);
@ -298,7 +297,7 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransfer()
*
* @return A status code based on the outcome of the data transfer attempt.
*/
transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel()
TransmissionStatusType TcpIpMeshBackend::attemptDataTransferKernel()
{
WiFiClient currClient;
currClient.setTimeout(_stationModeTimeoutMs);
@ -308,11 +307,11 @@ transmission_status_t TcpIpMeshBackend::attemptDataTransferKernel()
{
fullStop(currClient);
verboseModePrint(F("Server unavailable"));
return TS_CONNECTION_FAILED;
return TransmissionStatusType::CONNECTION_FAILED;
}
transmission_status_t transmissionOutcome = exchangeInfo(currClient);
if (transmissionOutcome <= 0)
TransmissionStatusType transmissionOutcome = exchangeInfo(currClient);
if (static_cast<int>(transmissionOutcome) <= 0)
{
verboseModePrint(F("Transmission failed during exchangeInfo."));
return transmissionOutcome;
@ -343,7 +342,7 @@ void TcpIpMeshBackend::initiateConnectionToAP(const String &targetSSID, int targ
* @return A status code based on the outcome of the connection and data transfer process.
*
*/
transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID)
TransmissionStatusType TcpIpMeshBackend::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID)
{
if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact.
{
@ -366,37 +365,36 @@ transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID,
verboseModePrint(F("Connecting... "), false);
initiateConnectionToAP(targetSSID, targetChannel, targetBSSID);
int connectionStartTime = millis();
int attemptNumber = 1;
ExpiringTimeTracker connectionAttemptTimeout([this](){ return _connectionAttemptTimeoutMs; });
int waitingTime = millis() - connectionStartTime;
while((WiFi.status() == WL_DISCONNECTED) && waitingTime <= _connectionAttemptTimeoutMs)
while((WiFi.status() == WL_DISCONNECTED) && !connectionAttemptTimeout)
{
if(waitingTime > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt.
if(connectionAttemptTimeout.elapsedTime() > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt.
{
verboseModePrint(F("... "), false);
WiFi.disconnect();
yield();
initiateConnectionToAP(targetSSID, targetChannel, targetBSSID);
attemptNumber++;
++attemptNumber;
}
delay(1);
waitingTime = millis() - connectionStartTime;
}
verboseModePrint(String(waitingTime));
verboseModePrint(String(connectionAttemptTimeout.elapsedTime()));
/* If the connection timed out */
if (WiFi.status() != WL_CONNECTED)
{
verboseModePrint(F("Timeout"));
return TS_CONNECTION_FAILED;
return TransmissionStatusType::CONNECTION_FAILED;
}
return attemptDataTransfer();
}
transmission_status_t TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo)
TransmissionStatusType TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo)
{
WiFi.disconnect();
yield();
@ -452,7 +450,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
if(WiFi.status() == WL_CONNECTED)
{
transmission_status_t transmissionResult = attemptDataTransfer();
TransmissionStatusType transmissionResult = attemptDataTransfer();
latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult));
getTransmissionOutcomesUpdateHook()(*this);
@ -474,7 +472,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
{
for(const TcpIpNetworkInfo &currentNetwork : constConnectionQueue())
{
transmission_status_t transmissionResult = initiateTransmission(currentNetwork);
TransmissionStatusType transmissionResult = initiateTransmission(currentNetwork);
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
@ -492,16 +490,16 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
attemptTransmission(message, scan, scanAllWiFiChannels, true, false);
}
transmission_status_t TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect, bool initialDisconnect)
TransmissionStatusType TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect, bool initialDisconnect)
{
MutexTracker mutexTracker(_tcpIpTransmissionMutex);
if(!mutexTracker.mutexCaptured())
{
assert(false && String(F("ERROR! TCP/IP transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.")));
return TS_CONNECTION_FAILED;
return TransmissionStatusType::CONNECTION_FAILED;
}
transmission_status_t transmissionResult = TS_CONNECTION_FAILED;
TransmissionStatusType transmissionResult = TransmissionStatusType::CONNECTION_FAILED;
setTemporaryMessage(message);
if(initialDisconnect)

View File

@ -43,14 +43,14 @@ public:
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which
* is the request string received from another node and returns the string to send back.
* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which
* is the response string received from another node. Returns a transmission status code as a transmission_status_t.
* is the response string received from another node. Returns a transmission status code as a TransmissionStatusType.
* @param networkFilter The callback handler for deciding which WiFi networks to connect to.
* @param meshPassword The WiFi password for the mesh network.
* @param ssidPrefix The prefix (first part) of the node SSID.
* @param ssidSuffix The suffix (last part) of the node SSID.
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is separate for each TcpIpMeshBackend instance.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection.
* This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
@ -91,7 +91,7 @@ public:
static std::vector<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.
*/
static bool latestTransmissionSuccessful();
@ -124,7 +124,7 @@ public:
*
* Note that if wifiChannel and BSSID are missing from recipientInfo, connection time will be longer.
*/
transmission_status_t attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect = true, bool initialDisconnect = false);
TransmissionStatusType attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect = true, bool initialDisconnect = false);
/**
* If any clients are connected, accept their requests and call the requestHandler function for each one.
@ -183,8 +183,8 @@ public:
*
* @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds.
*/
void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs);
int32_t getConnectionAttemptTimeout();
void setConnectionAttemptTimeout(uint32_t connectionAttemptTimeoutMs);
uint32_t getConnectionAttemptTimeout();
/**
* Set the timeout to use for transmissions when this TcpIpMeshBackend instance acts as a station (i.e. when connected to another AP).
@ -228,12 +228,12 @@ protected:
/**
* Will be true if a transmission initiated by a public method is in progress.
*/
static bool _tcpIpTransmissionMutex;
static std::shared_ptr<bool> _tcpIpTransmissionMutex;
/**
* 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.
@ -257,7 +257,7 @@ private:
uint16_t _serverPort;
WiFiServer _server;
uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections
int32_t _connectionAttemptTimeoutMs = 10000;
uint32_t _connectionAttemptTimeoutMs = 10000;
int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here.
uint32_t _apModeTimeoutMs = 4500;
@ -271,12 +271,12 @@ private:
void fullStop(WiFiClient &currClient);
void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL);
transmission_status_t connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL);
transmission_status_t exchangeInfo(WiFiClient &currClient);
TransmissionStatusType connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL);
TransmissionStatusType exchangeInfo(WiFiClient &currClient);
bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait);
transmission_status_t attemptDataTransfer();
transmission_status_t attemptDataTransferKernel();
transmission_status_t initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
TransmissionStatusType attemptDataTransfer();
TransmissionStatusType attemptDataTransferKernel();
TransmissionStatusType initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
void enterPostTransmissionState(bool concludingDisconnect);
};

View File

@ -27,6 +27,7 @@
#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 {
public:

View File

@ -24,13 +24,13 @@
#include "TransmissionOutcome.h"
TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t transmissionStatus)
TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, TransmissionStatusType transmissionStatus)
: NetworkInfoBase(origin), _transmissionStatus(transmissionStatus)
{ }
TransmissionOutcome::TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, transmission_status_t transmissionStatus)
TransmissionOutcome::TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, TransmissionStatusType transmissionStatus)
: NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden), _transmissionStatus(transmissionStatus)
{ }
void TransmissionOutcome::setTransmissionStatus(transmission_status_t transmissionStatus) { _transmissionStatus = transmissionStatus; }
transmission_status_t TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; }
void TransmissionOutcome::setTransmissionStatus(TransmissionStatusType transmissionStatus) { _transmissionStatus = transmissionStatus; }
TransmissionStatusType TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; }

View File

@ -28,27 +28,27 @@
#include <ESP8266WiFi.h>
#include "NetworkInfoBase.h"
typedef enum
enum class TransmissionStatusType
{
TS_CONNECTION_FAILED = -1,
TS_TRANSMISSION_FAILED = 0,
TS_TRANSMISSION_COMPLETE = 1
} transmission_status_t;
CONNECTION_FAILED = -1,
TRANSMISSION_FAILED = 0,
TRANSMISSION_COMPLETE = 1
};
class TransmissionOutcome : public NetworkInfoBase {
public:
TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t transmissionStatus);
TransmissionOutcome(const NetworkInfoBase &origin, TransmissionStatusType transmissionStatus);
TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, transmission_status_t transmissionStatus);
TransmissionOutcome(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden, TransmissionStatusType transmissionStatus);
void setTransmissionStatus(transmission_status_t transmissionStatus);
transmission_status_t transmissionStatus() const;
void setTransmissionStatus(TransmissionStatusType transmissionStatus);
TransmissionStatusType transmissionStatus() const;
private:
transmission_status_t _transmissionStatus;
TransmissionStatusType _transmissionStatus;
};
#endif

View File

@ -54,6 +54,13 @@
#include "NetworkInfo.h"
#include "TransmissionOutcome.h"
typedef enum
{
TS_CONNECTION_FAILED = -1,
TS_TRANSMISSION_FAILED = 0,
TS_TRANSMISSION_COMPLETE = 1
} transmission_status_t;
class TransmissionResult : public NetworkInfo {
public:
@ -63,11 +70,11 @@ public:
/**
* @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results.
*/
TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true);
TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true) __attribute__((deprecated));
TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus);
TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus) __attribute__((deprecated));
TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus);
TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus) __attribute__((deprecated));
TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus);
};

View File

@ -27,15 +27,15 @@
namespace
{
constexpr char chars[36] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
constexpr uint8_t charValues[75] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 10
constexpr char chars[36] PROGMEM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
constexpr uint8_t charValues[75] PROGMEM {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; // Lower case letters
}
namespace MeshTypeConversionFunctions
{
String uint64ToString(uint64_t number, byte base)
String uint64ToString(uint64_t number, const byte base)
{
assert(2 <= base && base <= 36);
@ -44,14 +44,14 @@ namespace MeshTypeConversionFunctions
if(base == 16)
{
do {
result += chars[ number % base ];
result += (char)pgm_read_byte(chars + number % base);
number >>= 4; // We could write number /= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from.
} while ( number );
}
else
{
do {
result += chars[ number % base ];
result += (char)pgm_read_byte(chars + number % base);
number /= base;
} while ( number );
}
@ -61,7 +61,7 @@ namespace MeshTypeConversionFunctions
return result;
}
uint64_t stringToUint64(const String &string, byte base)
uint64_t stringToUint64(const String &string, const byte base)
{
assert(2 <= base && base <= 36);
@ -72,7 +72,7 @@ namespace MeshTypeConversionFunctions
for(uint32_t i = 0; i < string.length(); ++i)
{
result <<= 4; // We could write result *= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from.
result += charValues[string.charAt(i) - '0'];
result += pgm_read_byte(charValues + string.charAt(i) - '0');
}
}
else
@ -80,14 +80,14 @@ namespace MeshTypeConversionFunctions
for(uint32_t i = 0; i < string.length(); ++i)
{
result *= base;
result += charValues[string.charAt(i) - '0'];
result += pgm_read_byte(charValues + string.charAt(i) - '0');
}
}
return result;
}
String uint8ArrayToHexString(const uint8_t *uint8Array, uint32_t arrayLength)
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength)
{
String hexString;
if(!hexString.reserve(2*arrayLength)) // Each uint8_t will become two characters (00 to FF)
@ -95,26 +95,26 @@ namespace MeshTypeConversionFunctions
for(uint32_t i = 0; i < arrayLength; ++i)
{
hexString += chars[ uint8Array[i] >> 4 ];
hexString += chars[ uint8Array[i] % 16 ];
hexString += (char)pgm_read_byte(chars + (uint8Array[i] >> 4));
hexString += (char)pgm_read_byte(chars + uint8Array[i] % 16 );
}
return hexString;
}
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, uint32_t arrayLength)
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength)
{
assert(hexString.length() >= arrayLength*2); // Each array element can hold two hexString characters
for(uint32_t i = 0; i < arrayLength; ++i)
{
uint8Array[i] = (charValues[hexString.charAt(i*2) - '0'] << 4) + charValues[hexString.charAt(i*2 + 1) - '0'];
uint8Array[i] = (pgm_read_byte(charValues + hexString.charAt(i*2) - '0') << 4) + pgm_read_byte(charValues + hexString.charAt(i*2 + 1) - '0');
}
return uint8Array;
}
String uint8ArrayToMultiString(uint8_t *uint8Array, uint32_t arrayLength)
String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength)
{
String multiString;
if(!multiString.reserve(arrayLength))
@ -137,7 +137,7 @@ namespace MeshTypeConversionFunctions
return multiString;
}
String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, uint32_t arrayLength)
String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength)
{
String multiString;
if(!multiString.reserve(arrayLength))
@ -174,7 +174,7 @@ namespace MeshTypeConversionFunctions
return result;
}
uint8_t *uint64ToMac(uint64_t macValue, uint8_t *macArray)
uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray)
{
assert(macValue <= 0xFFFFFFFFFFFF); // Overflow will occur if value can't fit within 6 bytes
@ -188,7 +188,7 @@ namespace MeshTypeConversionFunctions
return macArray;
}
uint8_t *uint64ToUint8Array(uint64_t value, uint8_t *resultArray)
uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray)
{
resultArray[7] = value;
resultArray[6] = value >> 8;
@ -214,27 +214,25 @@ namespace MeshTypeConversionFunctions
* Helper function for meshBackendCast.
*/
template <typename T>
T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, mesh_backend_t resultClassType)
T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, MeshBackendType resultClassType)
{
if(meshBackendBaseInstance && meshBackendBaseInstance->getClassType() == resultClassType)
{
return static_cast<T>(meshBackendBaseInstance);
}
else
{
return nullptr;
}
return nullptr;
}
template <>
EspnowMeshBackend *meshBackendCast<EspnowMeshBackend *>(MeshBackendBase *meshBackendBaseInstance)
{
return attemptPointerCast<EspnowMeshBackend *>(meshBackendBaseInstance, MB_ESP_NOW);
return attemptPointerCast<EspnowMeshBackend *>(meshBackendBaseInstance, MeshBackendType::ESP_NOW);
}
template <>
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.
* @return A string of "number" encoded in radix "base".
*/
String uint64ToString(uint64_t number, byte base = 16);
String uint64ToString(uint64_t number, const byte base = 16);
/**
* Note that using base 10 instead of 16 increases conversion time by roughly a factor of 2, due to unfavourable 64-bit arithmetic.
@ -52,7 +52,7 @@ namespace MeshTypeConversionFunctions
* @param base The radix of "string". Must be between 2 and 36.
* @return A uint64_t of the string, using radix "base" during decoding.
*/
uint64_t stringToUint64(const String &string, byte base = 16);
uint64_t stringToUint64(const String &string, const byte base = 16);
/**
* Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array.
@ -62,7 +62,7 @@ namespace MeshTypeConversionFunctions
* @param arrayLength The size of uint8Array, in bytes.
* @return Normally a String containing the HEX representation of the uint8Array. An empty String if the memory allocation for the String failed.
*/
String uint8ArrayToHexString(const uint8_t *uint8Array, uint32_t arrayLength);
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength);
/**
* Convert the contents of a String in HEX format to a uint8_t array. Index 0 of the array will represent the start of the String.
@ -73,7 +73,7 @@ namespace MeshTypeConversionFunctions
* @param arrayLength The number of bytes to fill in uint8Array.
* @return A pointer to the uint8Array.
*/
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, uint32_t arrayLength);
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength);
/**
* Stores the exact values of uint8Array in a String, even null values.
@ -86,7 +86,7 @@ namespace MeshTypeConversionFunctions
* @param arrayLength The size of uint8Array, in bytes.
* @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed.
*/
String uint8ArrayToMultiString(uint8_t *uint8Array, uint32_t arrayLength);
String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength);
/**
* Stores the exact values of uint8Array in a String, even null values.
@ -99,7 +99,7 @@ namespace MeshTypeConversionFunctions
* @param arrayLength The size of uint8Array, in bytes.
* @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed.
*/
String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, uint32_t arrayLength);
String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength);
/**
* Takes a uint8_t array and converts the first 6 bytes to a hexadecimal string.
@ -133,7 +133,7 @@ namespace MeshTypeConversionFunctions
* @param macArray A uint8_t array that will hold the mac address once the function returns. Should have a size of at least 6 bytes.
* @return The macArray.
*/
uint8_t *uint64ToMac(uint64_t macValue, uint8_t *macArray);
uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray);
/**
* Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB.
@ -142,7 +142,7 @@ namespace MeshTypeConversionFunctions
* @param resultArray A uint8_t array that will hold the result once the function returns. Should have a size of at least 8 bytes.
* @return The resultArray.
*/
uint8_t *uint64ToUint8Array(uint64_t value, uint8_t *resultArray);
uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray);
/**
* Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB.

View File

@ -30,7 +30,7 @@ namespace MeshUtilityFunctions
{
bool macEqual(const uint8_t *macOne, const uint8_t *macTwo)
{
for(int i = 0; i <= 5; i++)
for(int i = 0; i <= 5; ++i)
{
if(macOne[i] != macTwo[i])
{