1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-22 21:23:07 +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://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.
// 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 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);
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.
// 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.println(tcpIpInstance->getMessage().substring(0, 100));
Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100));
} else {
Serial.print("UNKNOWN!: ");
}
@ -127,13 +129,17 @@ void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) {
uint64_t targetNodeID = stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length()));
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.
@ -147,10 +153,8 @@ const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (E
@return True if the broadcast should be accepted. False otherwise.
*/
bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) {
/**
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.
*/
// 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.
int32_t metadataEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter);
@ -230,7 +234,7 @@ void loop() {
uint32_t startTime = millis();
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();
@ -239,23 +243,23 @@ void loop() {
espnowDelay(100);
// One way to check how attemptTransmission worked out
if (MeshBackendBase::latestTransmissionSuccessful()) {
if (espnowNode.latestTransmissionSuccessful()) {
Serial.println(F("Transmission successful."));
}
// Another way to check how attemptTransmission worked out
if (MeshBackendBase::latestTransmissionOutcomes.empty()) {
if (espnowNode.latestTransmissionOutcomes().empty()) {
Serial.println(F("No mesh AP found."));
} else {
for (TransmissionResult &transmissionResult : MeshBackendBase::latestTransmissionOutcomes) {
if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) {
Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionResult.SSID);
} else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) {
Serial.println(String(F("Connection failed to mesh AP ")) + transmissionResult.SSID);
} else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) {
for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) {
if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_FAILED) {
Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionOutcome.transmissionStatus() == TS_CONNECTION_FAILED) {
Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID());
} else if (transmissionOutcome.transmissionStatus() == TS_TRANSMISSION_COMPLETE) {
// No need to do anything, transmission was successful.
} 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);
}
}
@ -276,14 +280,16 @@ void loop() {
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.
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.
String peerMac = macToString(MeshBackendBase::connectionQueue[0].BSSID);
String peerMac = macToString(targetBSSID);
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;
Serial.println("\nTransmitting: " + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
@ -291,11 +297,11 @@ void loop() {
// 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.
String serializedEncryptedConnection = EspnowMeshBackend::serializeEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID);
String serializedEncryptedConnection = EspnowMeshBackend::serializeEncryptedConnection(targetBSSID);
Serial.println();
// 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.
espnowMessage = "This message is no longer encrypted when received by node " + peerMac;
@ -304,7 +310,7 @@ void loop() {
espnowDelay(100); // Wait for 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);
espnowMessage = "This message is once again encrypted when received by node " + peerMac;
@ -314,21 +320,28 @@ void loop() {
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(MeshBackendBase::connectionQueue[0].BSSID);
encrypted_connection_removal_outcome_t removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID);
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.
if (espnowNode.requestTemporaryEncryptedConnection(MeshBackendBase::connectionQueue[0].BSSID, 1000) == ECS_CONNECTION_ESTABLISHED) {
if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == ECS_CONNECTION_ESTABLISHED) {
espnowDelay(42);
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.";
Serial.println("\nTransmitting: " + espnowMessage);
espnowNode.attemptTransmission(espnowMessage, false);
EspnowMeshBackend::getConnectionInfo(MeshBackendBase::connectionQueue[0].BSSID, &remainingDuration);
EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration);
espnowDelay(remainingDuration + 100);
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<EspnowNetworkInfo> EspnowMeshBackend::_connectionQueue = {};
std::vector<TransmissionOutcome> EspnowMeshBackend::_latestTransmissionOutcomes = {};
uint32_t EspnowMeshBackend::_espnowTransmissionTimeoutMs = 40;
uint32_t EspnowMeshBackend::_espnowRetransmissionIntervalMs = 15;
@ -133,6 +136,16 @@ void EspnowMeshBackend::begin()
activateEspnow();
}
std::vector<EspnowNetworkInfo> & EspnowMeshBackend::connectionQueue()
{
return _connectionQueue;
}
std::vector<TransmissionOutcome> & EspnowMeshBackend::latestTransmissionOutcomes()
{
return _latestTransmissionOutcomes;
}
void EspnowMeshBackend::performEspnowMaintainance()
{
// 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);
return removeEncryptedConnectionUnprotected(connectionIterator, resultingIterator);
@ -1908,52 +1921,39 @@ uint8_t *EspnowMeshBackend::getEncryptedMac(const uint8_t *peerMac, uint8_t *res
}
}
void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
void EspnowMeshBackend::prepareForTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
{
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
if(!mutexTracker.mutexCaptured())
{
assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
return;
}
setMessage(message);
latestTransmissionOutcomes.clear();
latestTransmissionOutcomes().clear();
if(scan)
{
connectionQueue().clear();
scanForNetworks(scanAllWiFiChannels);
}
for(NetworkInfo &currentNetwork : connectionQueue)
{
String currentSSID = "";
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.
if(currentNetwork.BSSID != NULL)
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;
currentBSSID = currentNetwork.BSSID;
}
else // Use only networkIndex
transmission_status_t EspnowMeshBackend::initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo)
{
currentSSID = WiFi.SSID(currentNetwork.networkIndex);
currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex);
currentBSSID = WiFi.BSSID(currentNetwork.networkIndex);
}
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(currentNetwork.networkIndex, currentSSID, currentWiFiChannel);
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(getMessage(), currentBSSID);
transmission_status_t transmissionResult = sendRequest(message, targetBSSID);
uint32_t transmissionDuration = millis() - transmissionStartTime;
@ -1967,9 +1967,11 @@ void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bo
}
}
latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult});
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.");
@ -1981,6 +1983,82 @@ void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bo
}
}
void EspnowMeshBackend::attemptTransmission(const String &message, bool scan, bool scanAllWiFiChannels)
{
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
if(!mutexTracker.mutexCaptured())
{
assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
return;
}
prepareForTransmission(message, scan, scanAllWiFiChannels);
for(EspnowNetworkInfo &currentNetwork : connectionQueue())
{
transmission_status_t transmissionResult = initiateTransmission(getMessage(), currentNetwork);
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
}
printTransmissionStatistics();
}
transmission_status_t EspnowMeshBackend::attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo)
{
MutexTracker mutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
if(!mutexTracker.mutexCaptured())
{
assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
return TS_CONNECTION_FAILED;
}
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
{
printAPInfo(recipientInfo);
verboseModePrint(F(""));
}
*encryptedConnection = getEncryptedConnection(targetBSSID);
encrypted_connection_status_t connectionStatus = ECS_MAX_CONNECTIONS_REACHED_SELF;
if(createPermanentConnection)
connectionStatus = requestEncryptedConnection(targetBSSID);
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)
{
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);
}
}
void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, bool scan, bool scanAllWiFiChannels, bool createPermanentConnections)
{
MutexTracker outerMutexTracker(_espnowTransmissionMutex, handlePostponedRemovals);
@ -1990,19 +2068,16 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message,
return;
}
setMessage(message);
latestTransmissionOutcomes.clear();
if(scan)
{
scanForNetworks(scanAllWiFiChannels);
}
prepareForTransmission(message, scan, scanAllWiFiChannels);
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);
if(!innerMutexTracker.mutexCaptured())
{
@ -2010,87 +2085,34 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message,
return;
}
String currentSSID = "";
int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT;
uint8_t *currentBSSID = NULL;
transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus);
// 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)
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
finalizeAutoEncryptingConnection(currentBSSID, encryptedConnection, createPermanentConnections);
}
printTransmissionStatistics();
}
transmission_status_t EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, bool createPermanentConnection)
{
currentSSID = currentNetwork.SSID;
currentWiFiChannel = currentNetwork.wifiChannel;
currentBSSID = currentNetwork.BSSID;
}
else // Use only networkIndex
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())
{
currentSSID = WiFi.SSID(currentNetwork.networkIndex);
currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex);
currentBSSID = WiFi.BSSID(currentNetwork.networkIndex);
assert(false && "ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting.");
return TS_CONNECTION_FAILED;
}
if(verboseMode()) // Avoid string generation if not required
{
printAPInfo(currentNetwork.networkIndex, currentSSID, currentWiFiChannel);
verboseModePrint(F(""));
}
transmission_status_t transmissionResult = initiateAutoEncryptingTransmission(message, targetBSSID, connectionStatus);
EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(currentBSSID);
encrypted_connection_status_t connectionStatus = ECS_MAX_CONNECTIONS_REACHED_SELF;
finalizeAutoEncryptingConnection(targetBSSID, encryptedConnection, createPermanentConnection);
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
{
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.");
}
return transmissionResult;
}
void EspnowMeshBackend::broadcast(const String &message)

View File

@ -55,6 +55,7 @@
#include <map>
#include <list>
#include "Crypto.h"
#include "EspnowNetworkInfo.h"
typedef enum
{
@ -130,6 +131,24 @@ public:
~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.
*/
@ -178,6 +197,12 @@ public:
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,
* 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);
/**
* 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.
* A broadcast is always treated as a request by the receiving node.
@ -535,6 +566,9 @@ public:
protected:
static std::vector<EspnowNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
typedef std::vector<EncryptedConnectionLog>::iterator connectionLogIterator;
static connectionLogIterator connectionLogEndIterator();
@ -564,7 +598,7 @@ 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(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);
/**
@ -791,6 +825,15 @@ 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);
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
uint32_t totalDurationWhenSuccessful_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 "TypeConversionFunctions.h"
#include "MutexTracker.h"
#include <assert.h>
MeshBackendBase *MeshBackendBase::apController = nullptr;
std::vector<NetworkInfo> MeshBackendBase::connectionQueue = {};
std::vector<TransmissionResult> MeshBackendBase::latestTransmissionOutcomes = {};
bool MeshBackendBase::_scanMutex = false;
bool MeshBackendBase::_printWarnings = true;
@ -231,11 +232,11 @@ bool MeshBackendBase::getAPHidden() {return _apHidden;}
bool MeshBackendBase::latestTransmissionSuccessful()
{
if(MeshBackendBase::latestTransmissionOutcomes.empty())
if(latestTransmissionOutcomes().empty())
return false;
else
for(TransmissionResult &transmissionResult : MeshBackendBase::latestTransmissionOutcomes)
if(transmissionResult.transmissionStatus != TS_TRANSMISSION_COMPLETE)
for(TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes())
if(transmissionOutcome.transmissionStatus() != TS_TRANSMISSION_COMPLETE)
return false;
return true;
@ -243,10 +244,16 @@ bool MeshBackendBase::latestTransmissionSuccessful()
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);
/* Scan for APs */
connectionQueue.clear();
// 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.
@ -264,14 +271,20 @@ void MeshBackendBase::scanForNetworks(bool scanAllWiFiChannels)
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
{
mainNetworkIdentifier = macToString(apNetworkInfo.BSSID());
}
if(apNetworkIndex != NETWORK_INFO_DEFAULT_INT)
verboseModePrint(String(F("AP acquired: ")) + mainNetworkIdentifier + String(F(", Ch:")) + String(apNetworkInfo.wifiChannel()) + " ", false);
if(apNetworkInfo.RSSI() != NetworkInfoBase::defaultRSSI)
{
verboseModePrint("(" + String(WiFi.RSSI(apNetworkIndex)) + String(F("dBm) ")) +
(WiFi.encryptionType(apNetworkIndex) == ENC_TYPE_NONE ? String(F("open")) : ""), false);
verboseModePrint("(" + String(apNetworkInfo.RSSI()) + String(F("dBm) ")) +
(apNetworkInfo.encryptionType() == ENC_TYPE_NONE ? String(F("open")) : ""), false);
}
verboseModePrint(F("... "), false);

View File

@ -20,7 +20,8 @@
#define __MESHBACKENDBASE_H__
#include <ESP8266WiFi.h>
#include "TransmissionResult.h"
#include "TransmissionOutcome.h"
#include "NetworkInfoBase.h"
const String ESP8266_MESH_EMPTY_STRING = "";
@ -45,25 +46,18 @@ public:
virtual ~MeshBackendBase();
/**
* A vector that contains the NetworkInfo for each WiFi network to connect to.
* 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.
*/
static std::vector<NetworkInfo> connectionQueue;
/**
* A vector with the TransmissionResult for each AP to which a transmission was attempted during the latest attemptTransmission call.
* 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.
*/
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.
*/
static bool latestTransmissionSuccessful();
bool latestTransmissionSuccessful();
/**
* Initialises the node.
@ -271,7 +265,7 @@ public:
protected:
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.
@ -287,6 +281,8 @@ protected:
void setClassType(mesh_backend_t classType);
static bool _scanMutex;
private:
mesh_backend_t _classType;

View File

@ -23,6 +23,29 @@
* 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"
void NetworkInfo::copyBSSID(uint8_t newBSSID[6])
@ -74,4 +97,3 @@ NetworkInfo & NetworkInfo::operator=(const NetworkInfo &other)
networkIndex = other.networkIndex;
return *this;
}

View File

@ -23,12 +23,34 @@
* 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__
#define __NETWORKINFO_H__
#include <ESP8266WiFi.h>
const int NETWORK_INFO_DEFAULT_INT = -1;
#include "NetworkInfoBase.h"
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 = "";
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.
IPAddress TcpIpMeshBackend::staticIP = emptyIP;
IPAddress TcpIpMeshBackend::gateway = IPAddress(192,168,4,1);
IPAddress TcpIpMeshBackend::subnetMask = IPAddress(255,255,255,0);
std::vector<TcpIpNetworkInfo> TcpIpMeshBackend::_connectionQueue = {};
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)
@ -51,6 +56,16 @@ TcpIpMeshBackend::TcpIpMeshBackend(requestHandlerType requestHandler, responseHa
setServerPort(serverPort);
}
std::vector<TcpIpNetworkInfo> & TcpIpMeshBackend::connectionQueue()
{
return _connectionQueue;
}
std::vector<TransmissionOutcome> & TcpIpMeshBackend::latestTransmissionOutcomes()
{
return _latestTransmissionOutcomes;
}
void TcpIpMeshBackend::begin()
{
if(!TcpIpMeshBackend::getAPController()) // If there is no active AP controller
@ -78,6 +93,20 @@ void TcpIpMeshBackend::deactivateAPHook()
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)
{
// 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.
currClient.print(getMessage() + "\r");
currClient.print(getCurrentMessage() + "\r");
yield();
if (!waitForClientTransmission(currClient, _stationModeTimeoutMs))
@ -347,6 +376,41 @@ transmission_status_t TcpIpMeshBackend::connectToNode(const String &targetSSID,
return attemptDataTransfer();
}
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);
@ -364,66 +428,30 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, bool scan, boo
setMessage(message);
latestTransmissionOutcomes.clear();
latestTransmissionOutcomes().clear();
if(WiFi.status() == WL_CONNECTED)
{
transmission_status_t transmissionResult = attemptDataTransfer();
latestTransmissionOutcomes.push_back(TransmissionResult(connectionQueue.back(), transmissionResult));
latestTransmissionOutcomes().push_back(TransmissionOutcome(connectionQueue().back(), transmissionResult));
}
else
{
if(scan)
{
connectionQueue().clear();
scanForNetworks(scanAllWiFiChannels);
}
for(NetworkInfo &currentNetwork : connectionQueue)
for(TcpIpNetworkInfo &currentNetwork : connectionQueue())
{
WiFi.disconnect();
yield();
transmission_status_t transmissionResult = initiateTransmission(currentNetwork);
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 != "")
{
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});
latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult});
}
}
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();
}
enterPostTransmissionState(concludingDisconnect);
}
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);
}
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()
{
MutexTracker mutexTracker(_tcpIpTransmissionMutex);

View File

@ -31,7 +31,7 @@
#include <functional>
#include <vector>
#include "MeshBackendBase.h"
#include "NetworkInfo.h"
#include "TcpIpNetworkInfo.h"
class TcpIpMeshBackend : public MeshBackendBase {
@ -65,6 +65,24 @@ public:
const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, bool verboseMode = false,
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.
*/
@ -87,11 +105,25 @@ public:
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.
*/
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.
* The static IP needs to be at the same subnet as the server's gateway.
@ -165,6 +197,9 @@ public:
protected:
static std::vector<TcpIpNetworkInfo> _connectionQueue;
static std::vector<TransmissionOutcome> _latestTransmissionOutcomes;
/**
* Called just before we activate the AP.
* Put _server.stop() in deactivateAPHook() in case you use _server.begin() here.
@ -189,6 +224,16 @@ protected:
*/
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:
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.
uint32_t _apModeTimeoutMs = 4500;
static String _temporaryMessage;
static String lastSSID;
static bool staticIPActivated;
bool useStaticIP;
@ -212,6 +258,8 @@ private:
bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait);
transmission_status_t attemptDataTransfer();
transmission_status_t attemptDataTransferKernel();
transmission_status_t initiateTransmission(const TcpIpNetworkInfo &recipientInfo);
void enterPostTransmissionState(bool concludingDisconnect);
};
#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.
*/
/********************************************************************************************
* 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"
TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) :

View File

@ -23,18 +23,36 @@
* 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__
#define __TRANSMISSIONRESULT_H__
#include <ESP8266WiFi.h>
#include "NetworkInfo.h"
typedef enum
{
TS_CONNECTION_FAILED = -1,
TS_TRANSMISSION_FAILED = 0,
TS_TRANSMISSION_COMPLETE = 1
} transmission_status_t;
#include "TransmissionOutcome.h"
class TransmissionResult : public NetworkInfo {