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

- Make each mesh backend use a unique NetworkInfo class and separate connectionQueue and latestTransmissionOutcomes vectors.

- Deprecate NetworkInfo and TransmissionResult classes.

- Add single recipient transmission methods.

- Add a getCurrentMessage method to TcpIpMeshBackend to maintain feature parity when using single recipient transmission methods.

- Increase code abstraction level in transmission methods.

- Remove use of networkIndex except for in constructors, since it can change after each scan.

- Make Espnow backend require at least BSSID to connect, and the TcpIp backend require at least SSID.

- Make printAPInfo method take NetworkInfo as argument.

- Add new TransmissionOutcome class to replace obsolete TransmissionResult.

- Add _scanMutex.

- Improve code abstraction in HelloEspnow.ino.

- Update HelloEspnow.ino example to demonstrate the new features.

- Update and improve comments.
This commit is contained in:
Anders 2019-09-18 22:26:37 +02:00
parent 2576a0912b
commit 86025c7884
19 changed files with 998 additions and 255 deletions

View File

@ -14,7 +14,7 @@
https://github.com/esp8266/Arduino/issues/1143 https://github.com/esp8266/Arduino/issues/1143
https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html
*/ */
const char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes during ESP-NOW broadcasts and in the example networkFilter function below. const char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example networkFilter and broadcastFilter functions below.
const char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. const char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans.
// A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired. // A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired.
@ -32,6 +32,8 @@ uint8_t espnowHashKey[16] = {0xEF, 0x44, 0x33, 0x0C, 0x33, 0x44, 0xFE, 0x44, //
unsigned int requestNumber = 0; unsigned int requestNumber = 0;
unsigned int responseNumber = 0; 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); String manageRequest(const String &request, MeshBackendBase &meshInstance);
transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance); transmission_status_t manageResponse(const String &response, MeshBackendBase &meshInstance);
void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance);
@ -96,7 +98,7 @@ transmission_status_t manageResponse(const String &response, MeshBackendBase &me
// With ESP-NOW there is no guarantee when or if a response will show up, it can happen before or after the stored message is changed. // With ESP-NOW there is no guarantee when or if a response will show up, it can happen before or after the stored message is changed.
// So for ESP-NOW, adding unique identifiers in the response and request is required to associate a response with a request. // So for ESP-NOW, adding unique identifiers in the response and request is required to associate a response with a request.
Serial.print(F("Request sent: ")); Serial.print(F("Request sent: "));
Serial.println(tcpIpInstance->getMessage().substring(0, 100)); Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100));
} else { } else {
Serial.print("UNKNOWN!: "); Serial.print("UNKNOWN!: ");
} }
@ -127,14 +129,18 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
uint64_t targetNodeID = stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length())); uint64_t targetNodeID = stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length()));
if (targetNodeID < stringToUint64(meshInstance.getNodeID())) { if (targetNodeID < stringToUint64(meshInstance.getNodeID())) {
MeshBackendBase::connectionQueue.push_back(NetworkInfo(networkIndex)); if (EspnowMeshBackend *espnowInstance = meshBackendCast<EspnowMeshBackend *>(&meshInstance)) {
espnowInstance->connectionQueue().push_back(networkIndex);
} else if (TcpIpMeshBackend *tcpIpInstance = meshBackendCast<TcpIpMeshBackend *>(&meshInstance)) {
tcpIpInstance->connectionQueue().push_back(networkIndex);
} else {
Serial.println(String(F("Invalid mesh backend!")));
}
} }
} }
} }
} }
const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII
/** /**
Callback used to decide which broadcast messages to accept. Only called for the first transmission in each broadcast. Callback used to decide which broadcast messages to accept. Only called for the first transmission in each broadcast.
If true is returned from this callback, the first broadcast transmission is saved until the entire broadcast message has been received. If true is returned from this callback, the first broadcast transmission is saved until the entire broadcast message has been received.
@ -147,10 +153,8 @@ const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (E
@return True if the broadcast should be accepted. False otherwise. @return True if the broadcast should be accepted. False otherwise.
*/ */
bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) { bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) {
/** // This example broadcastFilter will accept a transmission if it contains the broadcastMetadataDelimiter
This example broadcastFilter will accept a transmission if it contains the broadcastMetadataDelimiter // and as metaData either no targetMeshName or a targetMeshName that matches the MeshName of meshInstance.
and as metaData either no targetMeshName or a targetMeshName that matches the MeshName of meshInstance.
*/
int32_t metadataEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter); int32_t metadataEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter);
@ -230,7 +234,7 @@ void loop() {
uint32_t startTime = millis(); uint32_t startTime = millis();
espnowNode.attemptTransmission(espnowNode.getMessage()); espnowNode.attemptTransmission(espnowNode.getMessage());
Serial.println("Scan and " + String(MeshBackendBase::latestTransmissionOutcomes.size()) + " transmissions done in " + String(millis() - startTime) + " ms."); Serial.println("Scan and " + String(espnowNode.latestTransmissionOutcomes().size()) + " transmissions done in " + String(millis() - startTime) + " ms.");
timeOfLastScan = millis(); timeOfLastScan = millis();
@ -239,23 +243,23 @@ void loop() {
espnowDelay(100); espnowDelay(100);
// One way to check how attemptTransmission worked out // One way to check how attemptTransmission worked out
if (MeshBackendBase::latestTransmissionSuccessful()) { if (espnowNode.latestTransmissionSuccessful()) {
Serial.println(F("Transmission successful.")); Serial.println(F("Transmission successful."));
} }
// Another way to check how attemptTransmission worked out // Another way to check how attemptTransmission worked out
if (MeshBackendBase::latestTransmissionOutcomes.empty()) { if (espnowNode.latestTransmissionOutcomes().empty()) {
Serial.println(F("No mesh AP found.")); Serial.println(F("No mesh AP found."));
} else { } else {
for (TransmissionResult &transmissionResult : MeshBackendBase::latestTransmissionOutcomes) { for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) {
if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) { if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_FAILED) {
Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionResult.SSID); Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) { } else if (transmissionOutcome.transmissionStatus() == TS_CONNECTION_FAILED) {
Serial.println(String(F("Connection failed to mesh AP ")) + transmissionResult.SSID); Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) { } else if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_COMPLETE) {
// No need to do anything, transmission was successful. // No need to do anything, transmission was successful.
} else { } else {
Serial.println(String(F("Invalid transmission status for ")) + transmissionResult.SSID + String(F("!"))); Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String(F("!")));
assert(F("Invalid transmission status returned from responseHandler!") && false); assert(F("Invalid transmission status returned from responseHandler!") && false);
} }
} }
@ -276,14 +280,16 @@ void loop() {
Serial.println("\nPerforming encrypted ESP-NOW transmissions."); Serial.println("\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. // We can create encrypted connections to individual nodes so that all ESP-NOW communication with the node will be encrypted.
if (espnowNode.requestEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID) == ECS_CONNECTION_ESTABLISHED) { if (espnowNode.connectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == ECS_CONNECTION_ESTABLISHED) {
// The WiFi scan will detect the AP MAC, but this will automatically be converted to the encrypted STA MAC by the framework. // The WiFi scan will detect the AP MAC, but this will automatically be converted to the encrypted STA MAC by the framework.
String peerMac = macToString(MeshBackendBase::connectionQueue[0].BSSID); String peerMac = macToString(targetBSSID);
Serial.println("Encrypted ESP-NOW connection with " + peerMac + " established!"); Serial.println("Encrypted ESP-NOW connection with " + peerMac + " established!");
// Making a transmission now will cause messages to MeshBackendBase::connectionQueue[0].BSSID to be encrypted. // Making a transmission now will cause messages to targetBSSID to be encrypted.
String espnowMessage = "This message is encrypted only when received by node " + peerMac; String espnowMessage = "This message is encrypted only when received by node " + peerMac;
Serial.println("\nTransmitting: " + espnowMessage); Serial.println("\nTransmitting: " + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
@ -291,11 +297,11 @@ void loop() {
// A connection can be serialized and stored for later use. // A connection can be serialized and stored for later use.
// Note that this saves the current state only, so if encrypted communication between the nodes happen after this, the stored state is invalid. // Note that this saves the current state only, so if encrypted communication between the nodes happen after this, the stored state is invalid.
String serializedEncryptedConnection = EspnowMeshBackend::serializeEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID); String serializedEncryptedConnection = EspnowMeshBackend::serializeEncryptedConnection(targetBSSID);
Serial.println(); Serial.println();
// We can remove an encrypted connection like so. // We can remove an encrypted connection like so.
espnowNode.removeEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID); espnowNode.removeEncryptedConnection(targetBSSID);
// Note that the peer will still be encrypted, so although we can send unencrypted messages to the peer, we cannot read the encrypted responses it sends back. // Note that the peer will still be encrypted, so although we can send unencrypted messages to the peer, we cannot read the encrypted responses it sends back.
espnowMessage = "This message is no longer encrypted when received by node " + peerMac; espnowMessage = "This message is no longer encrypted when received by node " + peerMac;
@ -304,7 +310,7 @@ void loop() {
espnowDelay(100); // Wait for response. espnowDelay(100); // Wait for response.
Serial.println("Cannot read the encrypted response..."); Serial.println("Cannot read the encrypted response...");
// Let's re-add our stored connection so we can communicate properly with MeshBackendBase::connectionQueue[0].BSSID again! // Let's re-add our stored connection so we can communicate properly with targetBSSID again!
espnowNode.addEncryptedConnection(serializedEncryptedConnection); espnowNode.addEncryptedConnection(serializedEncryptedConnection);
espnowMessage = "This message is once again encrypted when received by node " + peerMac; espnowMessage = "This message is once again encrypted when received by node " + peerMac;
@ -314,21 +320,28 @@ void loop() {
Serial.println(); Serial.println();
// If we want to remove the encrypted connection on both nodes, we can do it like this. // If we want to remove the encrypted connection on both nodes, we can do it like this.
encrypted_connection_removal_outcome_t removalOutcome = espnowNode.requestEncryptedConnectionRemoval(MeshBackendBase::connectionQueue[0].BSSID); encrypted_connection_removal_outcome_t removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID);
if (removalOutcome == ECRO_REMOVAL_SUCCEEDED) { if (removalOutcome == ECRO_REMOVAL_SUCCEEDED) {
Serial.println(peerMac + " is no longer encrypted!\n"); Serial.println(peerMac + " 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);
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. // Of course, we can also just create a temporary encrypted connection that will remove itself once its duration has passed.
if (espnowNode.requestTemporaryEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID, 1000) == ECS_CONNECTION_ESTABLISHED) { if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == ECS_CONNECTION_ESTABLISHED) {
espnowDelay(42); espnowDelay(42);
uint32_t remainingDuration = 0; uint32_t remainingDuration = 0;
EspnowMeshBackend::getConnectionInfo(MeshBackendBase::connectionQueue[0].BSSID, &remainingDuration); EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowMessage = "Messages this node sends to " + peerMac + " will be encrypted for " + String(remainingDuration) + " ms more."; espnowMessage = "Messages this node sends to " + peerMac + " will be encrypted for " + String(remainingDuration) + " ms more.";
Serial.println("\nTransmitting: " + espnowMessage); Serial.println("\nTransmitting: " + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false); espnowNode.attemptTransmission(espnowMessage, false);
EspnowMeshBackend::getConnectionInfo(MeshBackendBase::connectionQueue[0].BSSID, &remainingDuration); EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowDelay(remainingDuration + 100); espnowDelay(remainingDuration + 100);
espnowMessage = "Due to encrypted connection expiration, this message is no longer encrypted when received by node " + peerMac; espnowMessage = "Due to encrypted connection expiration, this message is no longer encrypted when received by node " + peerMac;

View File

@ -50,6 +50,9 @@ std::list<PeerRequestLog> EspnowMeshBackend::peerRequestConfirmationsToSend = {}
std::vector<EncryptedConnectionLog> EspnowMeshBackend::encryptedConnections = {}; std::vector<EncryptedConnectionLog> EspnowMeshBackend::encryptedConnections = {};
std::vector<EspnowNetworkInfo> EspnowMeshBackend::_connectionQueue = {};
std::vector<TransmissionOutcome> EspnowMeshBackend::_latestTransmissionOutcomes = {};
uint32_t EspnowMeshBackend::_espnowTransmissionTimeoutMs = 40; uint32_t EspnowMeshBackend::_espnowTransmissionTimeoutMs = 40;
uint32_t EspnowMeshBackend::_espnowRetransmissionIntervalMs = 15; uint32_t EspnowMeshBackend::_espnowRetransmissionIntervalMs = 15;
@ -133,6 +136,16 @@ void EspnowMeshBackend::begin()
activateEspnow(); activateEspnow();
} }
std::vector<EspnowNetworkInfo> & EspnowMeshBackend::connectionQueue()
{
return _connectionQueue;
}
std::vector<TransmissionOutcome> & EspnowMeshBackend::latestTransmissionOutcomes()
{
return _latestTransmissionOutcomes;
}
void EspnowMeshBackend::performEspnowMaintainance() void EspnowMeshBackend::performEspnowMaintainance()
{ {
// Doing this during an ESP-NOW transmission could invalidate iterators // Doing this during an ESP-NOW transmission could invalidate iterators
@ -1671,7 +1684,7 @@ encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnect
} }
} }
encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnectionUnprotected(uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator) encrypted_connection_removal_outcome_t EspnowMeshBackend::removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator)
{ {
connectionLogIterator connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections); connectionLogIterator connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections);
return removeEncryptedConnectionUnprotected(connectionIterator, resultingIterator); return removeEncryptedConnectionUnprotected(connectionIterator, resultingIterator);
@ -1908,6 +1921,68 @@ uint8_t *EspnowMeshBackend::getEncryptedMac(const uint8_t *peerMac, uint8_t *res
} }
} }
void EspnowMeshBackend::prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
{
setMessage(message);
latestTransmissionOutcomes().clear();
if(scan)
{
connectionQueue().clear();
scanForNetworks(scanAllWiFiChannels);
}
}
transmission_status_t EspnowMeshBackend::initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo)
{
uint8_t targetBSSID[6] {0};
assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect
recipientInfo.getBSSID(targetBSSID);
if(verboseMode()) // Avoid string generation if not required
{
printAPInfo(recipientInfo);
verboseModePrint(F(""));
}
return initiateTransmissionKernel(message, targetBSSID);
}
transmission_status_t EspnowMeshBackend::initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID)
{
uint32_t transmissionStartTime = millis();
transmission_status_t transmissionResult = sendRequest(message, targetBSSID);
uint32_t transmissionDuration = millis() - transmissionStartTime;
if(verboseMode() && transmissionResult == TS_TRANSMISSION_COMPLETE) // Avoid calculations if not required
{
totalDurationWhenSuccessful_AT += transmissionDuration;
successfulTransmissions_AT++;
if(transmissionDuration > maxTransmissionDuration_AT)
{
maxTransmissionDuration_AT = transmissionDuration;
}
}
return transmissionResult;
}
void EspnowMeshBackend::printTransmissionStatistics()
{
if(verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required
{
verboseModePrint("Average duration of successful transmissions: " + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + " ms.");
verboseModePrint("Maximum duration of successful transmissions: " + String(maxTransmissionDuration_AT) + " ms.");
}
else
{
verboseModePrint("No successful transmission.");
}
}
void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels) void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
{ {
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals); MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
@ -1917,67 +1992,70 @@ void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bo
return; return;
} }
setMessage(message); prepareForTransmission(message, scan, scanAllWiFiChannels);
latestTransmissionOutcomes.clear(); for(EspnowNetworkInfo &currentNetwork : connectionQueue())
if(scan)
{ {
scanForNetworks(scanAllWiFiChannels); transmission_status_t transmissionResult = initiateTransmission(getMessage(), currentNetwork);
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
} }
for(NetworkInfo &currentNetwork : connectionQueue) printTransmissionStatistics();
}
transmission_status_t EspnowMeshBackend::attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo)
{
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
if(!mutexTracker.mutexCaptured())
{ {
String currentSSID = ""; assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT; return TS_CONNECTION_FAILED;
uint8_t *currentBSSID = NULL;
// If a BSSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change.
if(currentNetwork.BSSID != NULL)
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;
currentBSSID = currentNetwork.BSSID;
}
else // Use only networkIndex
{
currentSSID = WiFi.SSID(currentNetwork.networkIndex);
currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex);
currentBSSID = WiFi.BSSID(currentNetwork.networkIndex);
}
if(verboseMode()) // Avoid string generation if not required
{
printAPInfo(currentNetwork.networkIndex, currentSSID, currentWiFiChannel);
verboseModePrint(F(""));
}
uint32_t transmissionStartTime = millis();
transmission_status_t transmissionResult = sendRequest(getMessage(), currentBSSID);
uint32_t transmissionDuration = millis() - transmissionStartTime;
if(verboseMode() && transmissionResult == TS_TRANSMISSION_COMPLETE) // Avoid calculations if not required
{
totalDurationWhenSuccessful_AT += transmissionDuration;
successfulTransmissions_AT++;
if(transmissionDuration > maxTransmissionDuration_AT)
{
maxTransmissionDuration_AT = transmissionDuration;
}
}
latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult});
} }
if(verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required return initiateTransmission(message, recipientInfo);
}
encrypted_connection_status_t EspnowMeshBackend::initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool createPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **encryptedConnection)
{
assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect
recipientInfo.getBSSID(targetBSSID);
if(verboseMode()) // Avoid string generation if not required
{ {
verboseModePrint("Average duration of successful transmissions: " + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + " ms."); printAPInfo(recipientInfo);
verboseModePrint("Maximum duration of successful transmissions: " + String(maxTransmissionDuration_AT) + " ms."); verboseModePrint(F(""));
} }
*encryptedConnection = getEncryptedConnection(targetBSSID);
encrypted_connection_status_t connectionStatus = ECS_MAX_CONNECTIONS_REACHED_SELF;
if(createPermanentConnection)
connectionStatus = requestEncryptedConnection(targetBSSID);
else else
connectionStatus = requestFlexibleTemporaryEncryptedConnection(targetBSSID, getAutoEncryptionDuration());
return connectionStatus;
}
transmission_status_t EspnowMeshBackend::initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, encrypted_connection_status_t connectionStatus)
{
transmission_status_t transmissionResult = TS_CONNECTION_FAILED;
if(connectionStatus == ECS_CONNECTION_ESTABLISHED)
{ {
verboseModePrint("No successful transmission."); transmissionResult = initiateTransmissionKernel(message, targetBSSID);
}
return transmissionResult;
}
void EspnowMeshBackend::finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *encryptedConnection, bool createPermanentConnection)
{
if(!encryptedConnection && !createPermanentConnection)
{
// Remove any connection that was added during the transmission attempt.
removeEncryptedConnectionUnprotected(targetBSSID);
} }
} }
@ -1990,19 +2068,16 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message,
return; return;
} }
setMessage(message); prepareForTransmission(message, scan, scanAllWiFiChannels);
latestTransmissionOutcomes.clear();
if(scan)
{
scanForNetworks(scanAllWiFiChannels);
}
outerMutexTracker.releaseMutex(); outerMutexTracker.releaseMutex();
for(NetworkInfo &currentNetwork : connectionQueue) for(EspnowNetworkInfo &currentNetwork : connectionQueue())
{ {
uint8_t currentBSSID[6] {0};
EncryptedConnectionLog *encryptedConnection = nullptr;
encrypted_connection_status_t connectionStatus = initiateAutoEncryptingConnection(currentNetwork, createPermanentConnections, currentBSSID, &encryptedConnection);
MutexTracker innerMutexTracker = MutexTracker(_espnowTransmissionMutex); MutexTracker innerMutexTracker = MutexTracker(_espnowTransmissionMutex);
if(!innerMutexTracker.mutexCaptured()) if(!innerMutexTracker.mutexCaptured())
{ {
@ -2010,87 +2085,34 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message,
return; return;
} }
String currentSSID = ""; transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus);
int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT;
uint8_t *currentBSSID = NULL;
// If a BSSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change. latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
if(currentNetwork.BSSID != NULL)
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;
currentBSSID = currentNetwork.BSSID;
}
else // Use only networkIndex
{
currentSSID = WiFi.SSID(currentNetwork.networkIndex);
currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex);
currentBSSID = WiFi.BSSID(currentNetwork.networkIndex);
}
if(verboseMode()) // Avoid string generation if not required finalizeAutoEncryptingConnection(currentBSSID, encryptedConnection, createPermanentConnections);
{
printAPInfo(currentNetwork.networkIndex, currentSSID, currentWiFiChannel);
verboseModePrint(F(""));
}
EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(currentBSSID);
encrypted_connection_status_t connectionStatus = ECS_MAX_CONNECTIONS_REACHED_SELF;
innerMutexTracker.releaseMutex();
if(createPermanentConnections)
connectionStatus = requestEncryptedConnection(currentBSSID);
else
connectionStatus = requestFlexibleTemporaryEncryptedConnection(currentBSSID, getAutoEncryptionDuration());
innerMutexTracker = MutexTracker(_espnowTransmissionMutex);
if(!innerMutexTracker.mutexCaptured())
{
assert(false && "ERROR! Unable to recapture Mutex in attemptAutoEncryptingTransmission. Aborting.");
return;
}
if(connectionStatus == ECS_CONNECTION_ESTABLISHED)
{
uint32_t transmissionStartTime = millis();
transmission_status_t transmissionResult = sendRequest(getMessage(), currentBSSID);
uint32_t transmissionDuration = millis() - transmissionStartTime;
if(verboseMode() && transmissionResult == TS_TRANSMISSION_COMPLETE) // Avoid calculations if not required
{
totalDurationWhenSuccessful_AT += transmissionDuration;
successfulTransmissions_AT++;
if(transmissionDuration > maxTransmissionDuration_AT)
{
maxTransmissionDuration_AT = transmissionDuration;
}
}
latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult});
}
else
{
latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = TS_CONNECTION_FAILED});
}
if(!encryptedConnection && !createPermanentConnections)
{
// Remove any connection that was added during the transmission attempt.
removeEncryptedConnectionUnprotected(currentBSSID);
}
} }
if(verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required printTransmissionStatistics();
}
transmission_status_t EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool createPermanentConnection)
{
uint8_t targetBSSID[6] {0};
EncryptedConnectionLog *encryptedConnection = nullptr;
encrypted_connection_status_t connectionStatus = initiateAutoEncryptingConnection(recipientInfo, createPermanentConnection, targetBSSID, &encryptedConnection);
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
if(!mutexTracker.mutexCaptured())
{ {
verboseModePrint("Average duration of successful transmissions: " + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + " ms."); assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
verboseModePrint("Maximum duration of successful transmissions: " + String(maxTransmissionDuration_AT) + " ms."); return TS_CONNECTION_FAILED;
}
else
{
verboseModePrint("No successful transmission.");
} }
transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(message, targetBSSID, connectionStatus);
finalizeAutoEncryptingConnection(targetBSSID, encryptedConnection, createPermanentConnection);
return transmissionResult;
} }
void EspnowMeshBackend::broadcast(const String &message) void EspnowMeshBackend::broadcast(const String &message)

View File

@ -55,6 +55,7 @@
#include <map> #include <map>
#include <list> #include <list>
#include "Crypto.h" #include "Crypto.h"
#include "EspnowNetworkInfo.h"
typedef enum typedef enum
{ {
@ -130,6 +131,24 @@ public:
~EspnowMeshBackend() override; ~EspnowMeshBackend() override;
/**
* Returns a vector that contains the NetworkInfo for each WiFi network to connect to.
* This vector is unique for each mesh backend.
* The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes.
* WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/
std::vector<EspnowNetworkInfo> & connectionQueue();
/**
* Returns a vector with the TransmissionOutcome for each AP to which a transmission was attempted during the latest attemptTransmission call.
* This vector is unique for each mesh backend.
* The latestTransmissionOutcomes vector is cleared before each new transmission attempt.
* Connection attempts are indexed in the same order they were attempted.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/
std::vector<TransmissionOutcome> & latestTransmissionOutcomes() override;
/** /**
* Initialises the node. * Initialises the node.
*/ */
@ -178,6 +197,12 @@ public:
void attemptTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false) override; void attemptTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false) override;
/**
* Transmit message to a single recipient without changing the local transmission state.
* Will not change connectionQueue, latestTransmissionOutcomes or stored message.
*/
transmission_status_t attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo);
/* /*
* Will ensure that an encrypted connection exists to each target node before sending the message, * Will ensure that an encrypted connection exists to each target node before sending the message,
* establishing a temporary encrypted connection with duration getAutoEncryptionDuration() first if neccessary. * establishing a temporary encrypted connection with duration getAutoEncryptionDuration() first if neccessary.
@ -201,6 +226,12 @@ public:
*/ */
void attemptAutoEncryptingTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false, bool createPermanentConnections = false); void attemptAutoEncryptingTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false, bool createPermanentConnections = false);
/**
* 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 createPermanentConnection = false);
/** /**
* Send a message simultaneously to all nearby nodes which have ESP-NOW activated. * Send a message simultaneously to all nearby nodes which have ESP-NOW activated.
* A broadcast is always treated as a request by the receiving node. * A broadcast is always treated as a request by the receiving node.
@ -535,6 +566,9 @@ public:
protected: protected:
static std::vector<EspnowNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
typedef std::vector<EncryptedConnectionLog>::iterator connectionLogIterator; typedef std::vector<EncryptedConnectionLog>::iterator connectionLogIterator;
static connectionLogIterator connectionLogEndIterator(); static connectionLogIterator connectionLogEndIterator();
@ -564,7 +598,7 @@ protected:
// Consider using getScheduledResponseRecipient and similar methods for this preparation. // Consider using getScheduledResponseRecipient and similar methods for this preparation.
// Should only be used when there is no transmissions in progress. In practice when _espnowTransmissionMutex is free. // Should only be used when there is no transmissions in progress. In practice when _espnowTransmissionMutex is free.
// @param resultingIterator Will be set to the iterator position after the removed element, if an element to remove was found. Otherwise no change will occur. // @param resultingIterator Will be set to the iterator position after the removed element, if an element to remove was found. Otherwise no change will occur.
static encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(uint8_t *peerMac, std::vector<EncryptedConnectionLog>::iterator *resultingIterator = nullptr); 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 encrypted_connection_removal_outcome_t removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector<EncryptedConnectionLog>::iterator *resultingIterator);
/** /**
@ -791,6 +825,15 @@ private:
*/ */
static uint64_t createSessionKey(); 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);
void printTransmissionStatistics();
encrypted_connection_status_t initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, bool createPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **encryptedConnection);
transmission_status_t initiateAutoEncryptingTransmission(const String &message, const uint8_t *targetBSSID, encrypted_connection_status_t connectionStatus);
void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *encryptedConnection, bool createPermanentConnection);
// Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter // Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter
uint32_t totalDurationWhenSuccessful_AT = 0; uint32_t totalDurationWhenSuccessful_AT = 0;
uint32_t successfulTransmissions_AT = 0; uint32_t successfulTransmissions_AT = 0;

View File

@ -0,0 +1,32 @@
/*
* 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 "EspnowNetworkInfo.h"
EspnowNetworkInfo::EspnowNetworkInfo(int networkIndex) : NetworkInfoBase(networkIndex) { };
EspnowNetworkInfo::EspnowNetworkInfo(const uint8_t BSSID[6], const String &SSID, int32_t wifiChannel, uint8_t encryptionType, int32_t RSSI , bool isHidden)
: NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden)
{ }

View File

@ -0,0 +1,43 @@
/*
* 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.
*/
#ifndef __ESPNOWNETWORKINFO_H__
#define __ESPNOWNETWORKINFO_H__
#include "NetworkInfoBase.h"
class EspnowNetworkInfo : public NetworkInfoBase {
public:
/**
* Automatically fill in the rest of the network info using networkIndex and the WiFi scan results.
*/
EspnowNetworkInfo(int networkIndex);
EspnowNetworkInfo(const uint8_t BSSID[6], const String &SSID = defaultSSID, int32_t wifiChannel = defaultWifiChannel, uint8_t encryptionType = defaultEncryptionType,
int32_t RSSI = defaultRSSI, bool isHidden = defaultIsHidden);
};
#endif

View File

@ -17,13 +17,14 @@
*/ */
#include "MeshBackendBase.h" #include "MeshBackendBase.h"
#include "TypeConversionFunctions.h"
#include "MutexTracker.h"
#include <assert.h> #include <assert.h>
MeshBackendBase *MeshBackendBase::apController = nullptr; MeshBackendBase *MeshBackendBase::apController = nullptr;
std::vector<NetworkInfo> MeshBackendBase::connectionQueue = {}; bool MeshBackendBase::_scanMutex = false;
std::vector<TransmissionResult> MeshBackendBase::latestTransmissionOutcomes = {};
bool MeshBackendBase::_printWarnings = true; bool MeshBackendBase::_printWarnings = true;
@ -231,11 +232,11 @@ bool MeshBackendBase::getAPHidden() {return _apHidden;}
bool MeshBackendBase::latestTransmissionSuccessful() bool MeshBackendBase::latestTransmissionSuccessful()
{ {
if(MeshBackendBase::latestTransmissionOutcomes.empty()) if(latestTransmissionOutcomes().empty())
return false; return false;
else else
for(TransmissionResult &transmissionResult : MeshBackendBase::latestTransmissionOutcomes) for(TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes())
if(transmissionResult.transmissionStatus != TS_TRANSMISSION_COMPLETE) if(transmissionOutcome.transmissionStatus() != TS_TRANSMISSION_COMPLETE)
return false; return false;
return true; return true;
@ -243,10 +244,16 @@ bool MeshBackendBase::latestTransmissionSuccessful()
void MeshBackendBase::scanForNetworks(bool scanAllWiFiChannels) void MeshBackendBase::scanForNetworks(bool scanAllWiFiChannels)
{ {
MutexTracker mutexTracker(_scanMutex);
if(!mutexTracker.mutexCaptured())
{
assert(false && "ERROR! Scan already in progress. Don't call scanForNetworks from callbacks as this may corrupt program state! Aborting.");
return;
}
verboseModePrint(F("Scanning... "), false); verboseModePrint(F("Scanning... "), false);
/* Scan for APs */ /* Scan for APs */
connectionQueue.clear();
// If scanAllWiFiChannels is true, scanning will cause the WiFi radio to cycle through all WiFi channels. // If scanAllWiFiChannels is true, scanning will cause the WiFi radio to cycle through all WiFi channels.
// This means existing WiFi connections are likely to break or work poorly if done frequently. // This means existing WiFi connections are likely to break or work poorly if done frequently.
@ -264,14 +271,20 @@ void MeshBackendBase::scanForNetworks(bool scanAllWiFiChannels)
getNetworkFilter()(n, *this); // Update the connectionQueue. getNetworkFilter()(n, *this); // Update the connectionQueue.
} }
void MeshBackendBase::printAPInfo(const int apNetworkIndex, const String &apSSID, const int apWiFiChannel) void MeshBackendBase::printAPInfo(const NetworkInfoBase &apNetworkInfo)
{ {
verboseModePrint(String(F("AP acquired: ")) + apSSID + String(F(", Ch:")) + String(apWiFiChannel) + " ", false); String mainNetworkIdentifier = apNetworkInfo.SSID();
if(mainNetworkIdentifier == NetworkInfoBase::defaultSSID) // If SSID not provided, use BSSID instead
if(apNetworkIndex != NETWORK_INFO_DEFAULT_INT)
{ {
verboseModePrint("(" + String(WiFi.RSSI(apNetworkIndex)) + String(F("dBm) ")) + mainNetworkIdentifier = macToString(apNetworkInfo.BSSID());
(WiFi.encryptionType(apNetworkIndex) == ENC_TYPE_NONE ? String(F("open")) : ""), false); }
verboseModePrint(String(F("AP acquired: ")) + mainNetworkIdentifier + String(F(", Ch:")) + String(apNetworkInfo.wifiChannel()) + " ", false);
if(apNetworkInfo.RSSI() != NetworkInfoBase::defaultRSSI)
{
verboseModePrint("(" + String(apNetworkInfo.RSSI()) + String(F("dBm) ")) +
(apNetworkInfo.encryptionType() == ENC_TYPE_NONE ? String(F("open")) : ""), false);
} }
verboseModePrint(F("... "), false); verboseModePrint(F("... "), false);

View File

@ -20,7 +20,8 @@
#define __MESHBACKENDBASE_H__ #define __MESHBACKENDBASE_H__
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "TransmissionResult.h" #include "TransmissionOutcome.h"
#include "NetworkInfoBase.h"
const String ESP8266_MESH_EMPTY_STRING = ""; const String ESP8266_MESH_EMPTY_STRING = "";
@ -45,25 +46,18 @@ public:
virtual ~MeshBackendBase(); virtual ~MeshBackendBase();
/** /**
* A vector that contains the NetworkInfo for each WiFi network to connect to. * Returns a vector with the TransmissionOutcome for each AP to which a transmission was attempted during the latest attemptTransmission call.
* The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. * This vector is unique for each mesh backend.
* WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/
static std::vector<NetworkInfo> connectionQueue;
/**
* A vector with the TransmissionResult for each AP to which a transmission was attempted during the latest attemptTransmission call.
* The latestTransmissionOutcomes vector is cleared before each new transmission attempt. * The latestTransmissionOutcomes vector is cleared before each new transmission attempt.
* Connection attempts are indexed in the same order they were attempted. * Connection attempts are indexed in the same order they were attempted.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. * Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/ */
static std::vector<TransmissionResult> latestTransmissionOutcomes; virtual std::vector<TransmissionOutcome> & latestTransmissionOutcomes() = 0;
/** /**
* @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 TS_TRANSMISSION_COMPLETE). False otherwise.
*/ */
static bool latestTransmissionSuccessful(); bool latestTransmissionSuccessful();
/** /**
* Initialises the node. * Initialises the node.
@ -271,7 +265,7 @@ public:
protected: protected:
virtual void scanForNetworks(bool scanAllWiFiChannels); virtual void scanForNetworks(bool scanAllWiFiChannels);
virtual void printAPInfo(const int apNetworkIndex, const String &apSSID, const int apWiFiChannel); virtual void printAPInfo(const NetworkInfoBase &apNetworkInfo);
/** /**
* Called just before we activate the AP. * Called just before we activate the AP.
@ -287,6 +281,8 @@ protected:
void setClassType(mesh_backend_t classType); void setClassType(mesh_backend_t classType);
static bool _scanMutex;
private: private:
mesh_backend_t _classType; mesh_backend_t _classType;

View File

@ -23,6 +23,29 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/********************************************************************************************
* NOTE!
*
* This class is deprecated and will be removed in core version 3.0.0.
* If you are still using this class, please consider migrating to the new API shown in
* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files.
*
* TODO: delete this file.
********************************************************************************************/
#include "NetworkInfo.h" #include "NetworkInfo.h"
void NetworkInfo::copyBSSID(uint8_t newBSSID[6]) void NetworkInfo::copyBSSID(uint8_t newBSSID[6])
@ -74,4 +97,3 @@ NetworkInfo & NetworkInfo::operator=(const NetworkInfo &other)
networkIndex = other.networkIndex; networkIndex = other.networkIndex;
return *this; return *this;
} }

View File

@ -23,12 +23,34 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/********************************************************************************************
* NOTE!
*
* This class is deprecated and will be removed in core version 3.0.0.
* If you are still using this class, please consider migrating to the new API shown in
* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files.
*
* TODO: delete this file.
********************************************************************************************/
#ifndef __NETWORKINFO_H__ #ifndef __NETWORKINFO_H__
#define __NETWORKINFO_H__ #define __NETWORKINFO_H__
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "NetworkInfoBase.h"
const int NETWORK_INFO_DEFAULT_INT = -1;
class NetworkInfo { class NetworkInfo {

View File

@ -0,0 +1,119 @@
/*
* 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 "NetworkInfoBase.h"
const String NetworkInfoBase::defaultSSID = "";
const int32_t NetworkInfoBase::defaultWifiChannel = NETWORK_INFO_DEFAULT_INT;
const uint8_t NetworkInfoBase::defaultEncryptionType = 0;
const int32_t NetworkInfoBase::defaultRSSI = ~0;
const bool NetworkInfoBase::defaultIsHidden = false;
void NetworkInfoBase::storeBSSID(const uint8_t newBSSID[6])
{
if(newBSSID != nullptr)
{
if(_BSSID == nullptr)
{
_BSSID = _bssidArray;
}
for(int i = 0; i < 6; i++)
{
_BSSID[i] = newBSSID[i];
}
}
else
{
_BSSID = nullptr;
}
}
NetworkInfoBase::NetworkInfoBase() {};
NetworkInfoBase::NetworkInfoBase(uint8_t networkIndex)
{
uint8_t *bssidPtr = nullptr;
WiFi.getNetworkInfo(networkIndex, _SSID, _encryptionType, _RSSI, bssidPtr, _wifiChannel, _isHidden);
storeBSSID(bssidPtr);
}
NetworkInfoBase::NetworkInfoBase(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden) :
_SSID(SSID), _wifiChannel(wifiChannel), _encryptionType(encryptionType), _RSSI(RSSI), _isHidden(isHidden)
{
storeBSSID(BSSID);
}
NetworkInfoBase::NetworkInfoBase(const NetworkInfoBase &other) : _SSID(other.SSID()), _wifiChannel(other.wifiChannel()), _encryptionType(other.encryptionType()),
_RSSI(other.RSSI()), _isHidden(other.isHidden())
{
storeBSSID(other.BSSID());
}
NetworkInfoBase & NetworkInfoBase::operator=(const NetworkInfoBase &other)
{
if(this != &other)
{
storeBSSID(other.BSSID());
_SSID = other.SSID();
_wifiChannel = other.wifiChannel();
_encryptionType = other.encryptionType();
_RSSI = other.RSSI();
_isHidden = other.isHidden();
}
return *this;
}
NetworkInfoBase::~NetworkInfoBase() { };
void NetworkInfoBase::setBSSID(const uint8_t BSSID[6]) { storeBSSID(BSSID); }
const uint8_t *NetworkInfoBase::BSSID() const { return _BSSID; }
uint8_t *NetworkInfoBase::getBSSID(uint8_t resultArray[6]) const
{
if(BSSID())
{
std::copy_n(_bssidArray, 6, resultArray);
return resultArray;
}
else
{
return nullptr;
}
}
void NetworkInfoBase::setSSID(String &SSID) { _SSID = SSID; }
String NetworkInfoBase::SSID() const { return _SSID; }
void NetworkInfoBase::setWifiChannel(int32_t wifiChannel) { _wifiChannel = wifiChannel; }
int32_t NetworkInfoBase::wifiChannel() const { return _wifiChannel; }
void NetworkInfoBase::setEncryptionType(uint8_t encryptionType) { _encryptionType = encryptionType; }
uint8_t NetworkInfoBase::encryptionType() const { return _encryptionType; }
void NetworkInfoBase::setRSSI(int32_t RSSI) { _RSSI = RSSI; }
int32_t NetworkInfoBase::RSSI() const { return _RSSI; }
void NetworkInfoBase::setIsHidden(bool isHidden) { _isHidden = isHidden; }
bool NetworkInfoBase::isHidden() const { return _isHidden; }

View File

@ -0,0 +1,101 @@
/*
* 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.
*/
#ifndef __NETWORKINFOBASE_H__
#define __NETWORKINFOBASE_H__
#include <ESP8266WiFi.h>
const int NETWORK_INFO_DEFAULT_INT = -1;
class NetworkInfoBase {
public:
/**
* Automatically fill in the rest of the network info using networkIndex and the WiFi scan results.
*/
NetworkInfoBase(uint8_t networkIndex);
/**
* Without giving channel and BSSID, connection time is longer.
*/
NetworkInfoBase(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI, bool isHidden);
NetworkInfoBase(const NetworkInfoBase &other);
NetworkInfoBase & operator=(const NetworkInfoBase &other);
void setBSSID(const uint8_t BSSID[6]);
const uint8_t *BSSID() const;
/**
* @return If BSSID is set, a pointer to resultArray which will contain a copy of BSSID. nullptr otherwise.
*/
uint8_t *getBSSID(uint8_t resultArray[6]) const;
void setSSID(String &SSID);
String SSID() const;
void setWifiChannel(int32_t wifiChannel);
int32_t wifiChannel() const;
void setEncryptionType(uint8_t encryptionType);
uint8_t encryptionType() const;
void setRSSI(int32_t RSSI);
int32_t RSSI() const;
void setIsHidden(bool isHidden);
bool isHidden() const;
static const String defaultSSID;
static const int32_t defaultWifiChannel;
static const uint8_t defaultEncryptionType;
static const int32_t defaultRSSI;
static const bool defaultIsHidden;
protected:
~NetworkInfoBase();
NetworkInfoBase();
/**
* Copy newBSSID into _BSSID.
* Prefer this method for changing NetworkInfo BSSID, unless you actually want to change the _BSSID pointer.
*/
void storeBSSID(const uint8_t newBSSID[6]);
private:
uint8_t _bssidArray[6] {0};
uint8_t *_BSSID = nullptr;
String _SSID = defaultSSID;
int32_t _wifiChannel = defaultWifiChannel;
uint8_t _encryptionType = defaultEncryptionType; // see enum wl_enc_type for values
int32_t _RSSI = defaultRSSI;
bool _isHidden = defaultIsHidden;
};
#endif

View File

@ -34,11 +34,16 @@ bool TcpIpMeshBackend::_tcpIpTransmissionMutex = false;
String TcpIpMeshBackend::lastSSID = ""; String TcpIpMeshBackend::lastSSID = "";
bool TcpIpMeshBackend::staticIPActivated = false; bool TcpIpMeshBackend::staticIPActivated = false;
String TcpIpMeshBackend::_temporaryMessage = "";
// IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server. // IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server.
IPAddress TcpIpMeshBackend::staticIP = emptyIP; IPAddress TcpIpMeshBackend::staticIP = emptyIP;
IPAddress TcpIpMeshBackend::gateway = IPAddress(192,168,4,1); IPAddress TcpIpMeshBackend::gateway = IPAddress(192,168,4,1);
IPAddress TcpIpMeshBackend::subnetMask = IPAddress(255,255,255,0); IPAddress TcpIpMeshBackend::subnetMask = IPAddress(255,255,255,0);
std::vector<TcpIpNetworkInfo> TcpIpMeshBackend::_connectionQueue = {};
std::vector<TransmissionOutcome> TcpIpMeshBackend::_latestTransmissionOutcomes = {};
TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler, TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHandlerType responseHandler,
networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix, networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix,
const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) const String &ssidSuffix, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort)
@ -51,6 +56,16 @@ TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHa
setServerPort(serverPort); setServerPort(serverPort);
} }
std::vector<TcpIpNetworkInfo> & TcpIpMeshBackend::connectionQueue()
{
return _connectionQueue;
}
std::vector<TransmissionOutcome> & TcpIpMeshBackend::latestTransmissionOutcomes()
{
return _latestTransmissionOutcomes;
}
void TcpIpMeshBackend::begin() void TcpIpMeshBackend::begin()
{ {
if(!TcpIpMeshBackend::getAPController()) // If there is no active AP controller if(!TcpIpMeshBackend::getAPController()) // If there is no active AP controller
@ -78,6 +93,20 @@ void TcpIpMeshBackend::deactivateAPHook()
bool TcpIpMeshBackend::transmissionInProgress(){return _tcpIpTransmissionMutex;} bool TcpIpMeshBackend::transmissionInProgress(){return _tcpIpTransmissionMutex;}
void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;}
String TcpIpMeshBackend::getTemporaryMessage() {return _temporaryMessage;}
void TcpIpMeshBackend::clearTemporaryMessage() {_temporaryMessage = "";}
String TcpIpMeshBackend::getCurrentMessage()
{
String message = getTemporaryMessage();
if(message == "") // If no temporary message stored
message = getMessage();
return message;
}
void TcpIpMeshBackend::setStaticIP(const IPAddress &newIP) void TcpIpMeshBackend::setStaticIP(const IPAddress &newIP)
{ {
// Comment out the line below to remove static IP and use DHCP instead. // Comment out the line below to remove static IP and use DHCP instead.
@ -201,7 +230,7 @@ transmission_status_t TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient)
{ {
verboseModePrint("Transmitting"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string. verboseModePrint("Transmitting"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string.
currClient.print(getMessage() + "\r"); currClient.print(getCurrentMessage() + "\r");
yield(); yield();
if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) if (!waitForClientTransmission(currClient, _stationModeTimeoutMs))
@ -347,7 +376,42 @@ transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID,
return attemptDataTransfer(); return attemptDataTransfer();
} }
void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels, bool concludingDisconnect, bool initialDisconnect ) transmission_status_t TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo)
{
WiFi.disconnect();
yield();
assert(recipientInfo.SSID() != ""); // We need at least SSID to connect
String targetSSID = recipientInfo.SSID();
int32_t targetWiFiChannel = recipientInfo.wifiChannel();
uint8_t targetBSSID[6] {0};
recipientInfo.getBSSID(targetBSSID);
if(verboseMode()) // Avoid string generation if not required
{
printAPInfo(recipientInfo);
}
return connectToNode(targetSSID, targetWiFiChannel, targetBSSID);
}
void TcpIpMeshBackend::enterPostTransmissionState(bool concludingDisconnect)
{
if(WiFi.status() == WL_CONNECTED && staticIP != emptyIP && !staticIPActivated)
{
verboseModePrint(F("Reactivating static IP to allow for faster re-connects."));
setStaticIP(staticIP);
}
// If we do not want to be connected at end of transmission, disconnect here so we can re-enable static IP first (above).
if(concludingDisconnect)
{
WiFi.disconnect();
yield();
}
}
void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels, bool concludingDisconnect, bool initialDisconnect)
{ {
MutexTracker mutexTracker(_tcpIpTransmissionMutex); MutexTracker mutexTracker(_tcpIpTransmissionMutex);
if(!mutexTracker.mutexCaptured()) if(!mutexTracker.mutexCaptured())
@ -364,66 +428,30 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
setMessage(message); setMessage(message);
latestTransmissionOutcomes.clear(); latestTransmissionOutcomes().clear();
if(WiFi.status() == WL_CONNECTED) if(WiFi.status() == WL_CONNECTED)
{ {
transmission_status_t transmissionResult = attemptDataTransfer(); transmission_status_t transmissionResult = attemptDataTransfer();
latestTransmissionOutcomes.push_back(TransmissionResult(connectionQueue.back(), transmissionResult)); latestTransmissionOutcomes().push_back(TransmissionOutcome(connectionQueue().back(), transmissionResult));
} }
else else
{ {
if(scan) if(scan)
{ {
connectionQueue().clear();
scanForNetworks(scanAllWiFiChannels); scanForNetworks(scanAllWiFiChannels);
} }
for(NetworkInfo &currentNetwork : connectionQueue) for(TcpIpNetworkInfo &currentNetwork : connectionQueue())
{ {
WiFi.disconnect(); transmission_status_t transmissionResult = initiateTransmission(currentNetwork);
yield();
String currentSSID = ""; latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
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 != "")
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;
currentBSSID = currentNetwork.BSSID;
}
else // Use only networkIndex
{
currentSSID = WiFi.SSID(currentNetwork.networkIndex);
currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex);
currentBSSID = WiFi.BSSID(currentNetwork.networkIndex);
}
if(verboseMode()) // Avoid string generation if not required
{
printAPInfo(currentNetwork.networkIndex, currentSSID, currentWiFiChannel);
}
transmission_status_t transmissionResult = connectToNode(currentSSID, currentWiFiChannel, currentBSSID);
latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult});
} }
} }
if(WiFi.status() == WL_CONNECTED && staticIP != emptyIP && !staticIPActivated) enterPostTransmissionState(concludingDisconnect);
{
verboseModePrint(F("Reactivating static IP to allow for faster re-connects."));
setStaticIP(staticIP);
}
// If we do not want to be connected at end of transmission, disconnect here so we can re-enable static IP first (above).
if(concludingDisconnect)
{
WiFi.disconnect();
yield();
}
} }
void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels) void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
@ -431,6 +459,39 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
attemptTransmission(message, scan, scanAllWiFiChannels, true, false); attemptTransmission(message, scan, scanAllWiFiChannels, true, false);
} }
transmission_status_t TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, bool concludingDisconnect, bool initialDisconnect)
{
MutexTracker mutexTracker(_tcpIpTransmissionMutex);
if(!mutexTracker.mutexCaptured())
{
assert(false && "ERROR! TCP/IP transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
return TS_CONNECTION_FAILED;
}
transmission_status_t transmissionResult = TS_CONNECTION_FAILED;
setTemporaryMessage(message);
if(initialDisconnect)
{
WiFi.disconnect();
yield();
}
if(WiFi.status() == WL_CONNECTED && WiFi.SSID() == recipientInfo.SSID())
{
transmissionResult = attemptDataTransfer();
}
else
{
transmissionResult = initiateTransmission(recipientInfo);
}
enterPostTransmissionState(concludingDisconnect);
clearTemporaryMessage();
return transmissionResult;
}
void TcpIpMeshBackend::acceptRequest() void TcpIpMeshBackend::acceptRequest()
{ {
MutexTracker mutexTracker(_tcpIpTransmissionMutex); MutexTracker mutexTracker(_tcpIpTransmissionMutex);

View File

@ -31,7 +31,7 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "MeshBackendBase.h" #include "MeshBackendBase.h"
#include "NetworkInfo.h" #include "TcpIpNetworkInfo.h"
class TcpIpMeshBackend : public MeshBackendBase { class TcpIpMeshBackend : public MeshBackendBase {
@ -65,6 +65,24 @@ public:
const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode = false, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode = false,
uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011); uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011);
/**
* Returns a vector that contains the NetworkInfo for each WiFi network to connect to.
* This vector is unique for each mesh backend.
* The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes.
* WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/
std::vector<TcpIpNetworkInfo> & connectionQueue();
/**
* Returns a vector with the TransmissionOutcome for each AP to which a transmission was attempted during the latest attemptTransmission call.
* This vector is unique for each mesh backend.
* The latestTransmissionOutcomes vector is cleared before each new transmission attempt.
* Connection attempts are indexed in the same order they were attempted.
* Note that old network indicies often are invalidated whenever a new WiFi network scan occurs.
*/
std::vector<TransmissionOutcome> & latestTransmissionOutcomes() override;
/** /**
* Initialises the node. * Initialises the node.
*/ */
@ -87,11 +105,25 @@ public:
void attemptTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false) override; void attemptTransmission(const String &message, bool scan = true, bool scanAllWiFiChannels = false) override;
/**
* Transmit message to a single recipient without changing the local transmission state (apart from connecting to the recipient if required).
* Will not change connectionQueue, latestTransmissionOutcomes or stored message.
*
* 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);
/** /**
* If any clients are connected, accept their requests and call the requestHandler function for each one. * If any clients are connected, accept their requests and call the requestHandler function for each one.
*/ */
void acceptRequest(); void acceptRequest();
/**
* Get the TCP/IP message that is currently scheduled for transmission.
* Unlike the getMessage() method, this will be correct even when the single recipient attemptTransmission method is used.
*/
String getCurrentMessage();
/** /**
* Set a static IP address for the ESP8266 and activate use of static IP. * Set a static IP address for the ESP8266 and activate use of static IP.
* The static IP needs to be at the same subnet as the server's gateway. * The static IP needs to be at the same subnet as the server's gateway.
@ -165,6 +197,9 @@ public:
protected: protected:
static std::vector<TcpIpNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
/** /**
* Called just before we activate the AP. * Called just before we activate the AP.
* Put _server.stop() in deactivateAPHook() in case you use _server.begin() here. * Put _server.stop() in deactivateAPHook() in case you use _server.begin() here.
@ -189,6 +224,16 @@ protected:
*/ */
static bool transmissionInProgress(); static bool transmissionInProgress();
/**
* Set a message that will be sent to other nodes when calling attemptTransmission, instead of the regular getMessage().
* This message is used until clearTemporaryMessage() is called.
*
* @param newMessage The message to send.
*/
void setTemporaryMessage(const String &newMessage);
String getTemporaryMessage();
void clearTemporaryMessage();
private: private:
uint16_t _serverPort; uint16_t _serverPort;
@ -198,6 +243,7 @@ private:
int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here.
uint32_t _apModeTimeoutMs = 4500; uint32_t _apModeTimeoutMs = 4500;
static String _temporaryMessage;
static String lastSSID; static String lastSSID;
static bool staticIPActivated; static bool staticIPActivated;
bool useStaticIP; bool useStaticIP;
@ -212,6 +258,8 @@ private:
bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait);
transmission_status_t attemptDataTransfer(); transmission_status_t attemptDataTransfer();
transmission_status_t attemptDataTransferKernel(); transmission_status_t attemptDataTransferKernel();
transmission_status_t initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
void enterPostTransmissionState(bool concludingDisconnect);
}; };
#endif #endif

View File

@ -0,0 +1,31 @@
/*
* 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 "TcpIpNetworkInfo.h"
TcpIpNetworkInfo::TcpIpNetworkInfo(int networkIndex) : NetworkInfoBase(networkIndex) { };
TcpIpNetworkInfo::TcpIpNetworkInfo(const String &SSID, int32_t wifiChannel, const uint8_t BSSID[6], uint8_t encryptionType, int32_t RSSI , bool isHidden)
: NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden)
{ }

View File

@ -0,0 +1,46 @@
/*
* 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.
*/
#ifndef __TCPIPNETWORKINFO_H__
#define __TCPIPNETWORKINFO_H__
#include "NetworkInfoBase.h"
class TcpIpNetworkInfo : public NetworkInfoBase {
public:
/**
* Automatically fill in the rest of the network info using networkIndex and the WiFi scan results.
*/
TcpIpNetworkInfo(int networkIndex);
/**
* Without giving wifiChannel and BSSID, connection time is longer.
*/
TcpIpNetworkInfo(const String &SSID, int32_t wifiChannel = defaultWifiChannel, const uint8_t BSSID[6] = nullptr, uint8_t encryptionType = defaultEncryptionType,
int32_t RSSI = defaultRSSI, bool isHidden = defaultIsHidden);
};
#endif

View File

@ -0,0 +1,36 @@
/*
* 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 "TransmissionOutcome.h"
TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t 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)
: 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; }

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef __TRANSMISSIONOUTCOME_H__
#define __TRANSMISSIONOUTCOME_H__
#include <ESP8266WiFi.h>
#include "NetworkInfoBase.h"
typedef enum
{
TS_CONNECTION_FAILED = -1,
TS_TRANSMISSION_FAILED = 0,
TS_TRANSMISSION_COMPLETE = 1
} transmission_status_t;
class TransmissionOutcome : public NetworkInfoBase {
public:
TransmissionOutcome(const NetworkInfoBase &origin, transmission_status_t 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);
void setTransmissionStatus(transmission_status_t transmissionStatus);
transmission_status_t transmissionStatus() const;
private:
transmission_status_t _transmissionStatus;
};
#endif

View File

@ -23,6 +23,29 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/********************************************************************************************
* NOTE!
*
* This class is deprecated and will be removed in core version 3.0.0.
* If you are still using this class, please consider migrating to the new API shown in
* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files.
*
* TODO: delete this file.
********************************************************************************************/
#include "TransmissionResult.h" #include "TransmissionResult.h"
TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) : TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) :

View File

@ -23,18 +23,36 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/********************************************************************************************
* NOTE!
*
* This class is deprecated and will be removed in core version 3.0.0.
* If you are still using this class, please consider migrating to the new API shown in
* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files.
*
* TODO: delete this file.
********************************************************************************************/
#ifndef __TRANSMISSIONRESULT_H__ #ifndef __TRANSMISSIONRESULT_H__
#define __TRANSMISSIONRESULT_H__ #define __TRANSMISSIONRESULT_H__
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "NetworkInfo.h" #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 { class TransmissionResult : public NetworkInfo {