Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update of mesh network library. #4718

Merged
merged 29 commits into from
Aug 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8e9a2d2
Make mesh network actually usable. Make mesh network use static IP du…
aerlon May 10, 2018
139c4de
Fix compiler warnings. Fix code style of HelloMesh.ino to avoid upset…
aerlon May 10, 2018
b1c3236
Remove stray spaces.
aerlon May 10, 2018
c6397f5
Make mesh network WiFi password settable via the ESP8266WiFiMesh cons…
aerlon May 11, 2018
e2816cf
Merge branch 'master' into wifi_mesh_update
earlephilhower May 17, 2018
ec13de5
Merge branch 'master' into wifi_mesh_update
devyte May 25, 2018
2533a33
Merge branch 'master' into wifi_mesh_update
aerlon Jun 4, 2018
4fb131f
Increase specificity in the conditions of the waitForClientTransmissi…
aerlon Jun 5, 2018
350b8e7
Merge branch 'master' into wifi_mesh_update
devyte Jun 7, 2018
0f9155e
Merge branch 'master' into wifi_mesh_update
devyte Jul 3, 2018
757d1f7
Merge branch 'master' into wifi_mesh_update
aerlon Jul 8, 2018
b5ea495
Improve most parts of the library to achieve better performance and g…
aerlon Jul 9, 2018
9c4ebc0
Improve README.rst formatting.
aerlon Jul 10, 2018
f03724a
Further improve README.rst.
aerlon Jul 10, 2018
b19f619
Even further improve README.rst.
aerlon Jul 10, 2018
3fccdf9
Make source code comments Doxygen compatible. Improve README file and…
aerlon Jul 22, 2018
367a680
Add temporary compatibility layer to ensure backwards compatibility w…
aerlon Jul 22, 2018
59418c3
Polish documentation slightly.
aerlon Jul 22, 2018
d3153ec
Merge branch 'master' into wifi_mesh_update
aerlon Jul 22, 2018
7f853a5
Add scan_all_wifi_channels option to attemptTransmission method.
aerlon Jul 23, 2018
887246e
Merge branch 'master' into wifi_mesh_update
d-a-v Jul 30, 2018
751f9af
- Add getter and setter for the WiFi channel of a ESP8266WiFiMesh ins…
aerlon Jul 31, 2018
ed15ab2
Make the code more stylish.
aerlon Jul 31, 2018
75a5549
Update README.md with the new ESP8266WiFiMesh constructor documentation.
aerlon Jul 31, 2018
a0edf99
Make attemptScan method in CompatibilityLayer use reference as argument.
aerlon Jul 31, 2018
40a25e6
Make it possible to use const String as argument to attemptScan.
aerlon Jul 31, 2018
0eadbb0
- Make code use camelCase instead of snake_case.
aerlon Jul 31, 2018
b6fc4bb
Rename Uint64ToString to uint64ToString and StringToUint64 to stringT…
aerlon Jul 31, 2018
cdbf7eb
Merge branch 'master' into wifi_mesh_update
devyte Aug 1, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions libraries/ESP8266WiFiMesh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
ESP8266 WiFi Mesh
=================

A library for turning your ESP8266 into a mesh network node.

The library has been tested and works with Arduino core for ESP8266 version 2.3.0 (with default lwIP) and 2.4.2 or higher (with lwIP 1.4 and lwIP2).

**Note:** This mesh library has been rewritten for core release 2.4.2. The old method signatures have been retained for compatibility purposes, but will be removed in core release 2.5.0. If you are still using these old method signatures please consider migrating to the new API shown in the `ESP8266WiFiMesh.h` source file.

Usage
-----

The basic operation of a mesh node is as follows:

The `attemptTransmission` method of the ESP8266WiFiMesh instance is called with a message to send to other nodes in the mesh network. If the node is already connected to an AP, the message is sent only to that AP. Otherwise a WiFi scan is performed. The scan results are sent to the `networkFilter` callback function of the ESP8266WiFiMesh instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the networks in the `connectionQueue`, and the response from each AP is sent to the `responseHandler` callback of the ESP8266WiFiMesh instance. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector.

The node receives messages from other nodes by calling the `acceptRequest` method of the ESP8266WiFiMesh instance. These received messages are passed to the `requestHandler` callback of the mesh instance. For each received message the return value of `requestHandler` is sent to the other node as a response to the message.

For more details, see the included example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`) and `networkFilter`. There is also more information to be found in the source code comments. An example is the ESP8266WiFiMesh constructor comment, which is shown below for reference:
```
/**
* WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised.
*
* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which
* is the request string received from another node and returns the string to send back.
* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which
* is the response string received from another node. Returns a transmission status code as a transmission_status_t.
* @param networkFilter The callback handler for deciding which WiFi networks to connect to.
* @param meshPassword The WiFi password for the mesh network.
* @param meshName The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example network filter function.
* @param nodeID The id for this mesh node. Used as suffix for the node SSID. If set to "", the id will default to ESP.getChipId().
* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default.
* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1.
* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection.
* This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels.
* In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the
* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly
* make it impossible for other stations to detect the APs whose WiFi channels have changed.
* @param serverPort The server port used by the AP of the ESP8266WiFiMesh instance. If multiple APs exist on a single ESP8266, each requires a separate server port.
* If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time.
* This is managed automatically by the activateAP method.
*
*/
ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter,
const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false,
uint8 meshWiFiChannel = 1, int serverPort = 4011);
```

### Note

* This library can use static IP:s for the nodes to speed up connection times. To enable this, use the `setStaticIP` method after calling the `begin` method, as in the included example. Ensure that nodes connecting to the same AP have distinct static IP:s. Node IP:s need to be at the same subnet as the server gateway (192.168.4 for this library by default). It may also be worth noting that station gateway IP must match the IP for the server on the nodes, though this is the default setting for the library.

At the moment static IP is a global setting, meaning that all ESP8266WiFiMesh instances on a single ESP8266 share the same static IP settings.

* When Arduino core for ESP8266 version 2.4.2 or higher is used, there are optimizations available for WiFi scans and static IP use to reduce the time it takes for nodes to connect to each other. These optimizations are enabled by default. To take advantage of the static IP optimizations you also need to use lwIP2. The lwIP version can be changed in the Tools menu of Arduino IDE.

If you are using a core version prior to 2.4.2 it is possible to disable the WiFi scan and static IP optimizations by commenting out the `ENABLE_STATIC_IP_OPTIMIZATION` and `ENABLE_WIFI_SCAN_OPTIMIZATION` defines in ESP8266WiFiMesh.h. Press Ctrl+K in the Arduino IDE while an example from the mesh library is opened, to open the library folder (or click "Show Sketch Folder" in the Sketch menu). ESP8266WiFiMesh.h can then be found at ESP8266WiFiMesh/src. Edit the file with any text editor.

* The WiFi scan optimization mentioned above works by making WiFi scans only search through the same WiFi channel as the ESP8266WiFiMesh instance is using. If you would like to scan all WiFi channels instead, set the `scanAllWiFiChannels` argument of the `attemptTransmission` method to `true`. Note that scanning all WiFi channels will slow down scans considerably and make it more likely that existing WiFi connections will break during scans. Also note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to (compare next bullet point). This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. To remedy this, force the AP back on the original channel by using the `restartAP` method of the current AP controller once the ESP8266 has disconnected from the other AP. This would typically be done like so:

```
if(ESP8266WiFiMesh *apController = ESP8266WiFiMesh::getAPController()) // Make sure apController is not nullptr
apController->restartAP();
```

* It is possible to have several ESP8266WiFiMesh instances running on every ESP8266 (e.g. to communicate with different mesh networks). However, because the ESP8266 has one WiFi radio only one AP per ESP8266 can be active at a time. Also note that if the ESP8266WiFiMesh instances use different WiFi channels, active APs are forced to use the same WiFi channel as active stations, possibly causing AP disconnections.

* While it is possible to connect to other nodes by only giving their SSID, e.g. `ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo("NodeSSID"));`, it is recommended that AP WiFi channel and AP BSSID are given as well, to minimize connection delay.

* Also, remember to change the default mesh network WiFi password!

General Information
---------------------------

* This library uses the standard Arduino core for ESP8266 WiFi functions. Therefore, other code that also uses these WiFi functions may cause conflicts with the library, resulting in strange behaviour.

* A maximum of 5 stations can be connected at a time to each AP.

* Unlike `WiFi.mode(WIFI_AP)`, the `WiFi.mode(WIFI_AP_STA)` which is used in this library allows nodes to stay connected to an AP they connect to while in STA mode, at the same time as they can receive connections from other stations. Nodes cannot send data to an AP while in STA_AP mode though, that requires STA mode. Switching to STA mode will sometimes disconnect stations connected to the node AP (though they can request a reconnect even while the previous AP node is in STA mode).

* Scanning for networks (e.g. via the `attemptTransmission` method) without the WiFi scan optimizations for core version 2.4.2 mentioned above, causes the WiFi radio to cycle through all WiFi channels which means existing WiFi connections are likely to break or work poorly if done frequently.
123 changes: 102 additions & 21 deletions libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino
Original file line number Diff line number Diff line change
@@ -1,50 +1,131 @@
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMesh.h>

unsigned int request_i = 0;
unsigned int response_i = 0;
String exampleMeshName("MeshNode_");

String manageRequest(String request);
unsigned int requestNumber = 0;
unsigned int responseNumber = 0;

String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance);
transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance);
void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance);

/* Create the mesh node object */
ESP8266WiFiMesh mesh_node = ESP8266WiFiMesh(ESP.getChipId(), manageRequest);
ESP8266WiFiMesh meshNode = ESP8266WiFiMesh(manageRequest, manageResponse, networkFilter, "ChangeThisWiFiPassword_TODO", exampleMeshName, "", true);

/**
Callback for when other nodes send you data
Callback for when other nodes send you a request

@request The string received from another node in the mesh
@param request The request string received from another node in the mesh
@param meshInstance The ESP8266WiFiMesh instance that called the function.
@returns The string to send back to the other node
*/
String manageRequest(String request) {
String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance) {
/* Print out received message */
Serial.print("received: ");
Serial.print("Request received: ");
Serial.println(request);

/* return a string to send back */
char response[60];
sprintf(response, "Hello world response #%d from Mesh_Node%d.", response_i++, ESP.getChipId());
return response;
return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + ".");
}

/**
Callback used to decide which networks to connect to once a WiFi scan has been completed.

@param numberOfNetworks The number of networks found in the WiFi scan.
@param meshInstance The ESP8266WiFiMesh instance that called the function.
*/
void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance) {
for (int i = 0; i < numberOfNetworks; ++i) {
String currentSSID = WiFi.SSID(i);
int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName());

/* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */
if (meshNameIndex >= 0) {
uint64_t targetNodeID = ESP8266WiFiMesh::stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length()));

if (targetNodeID < ESP8266WiFiMesh::stringToUint64(meshInstance.getNodeID())) {
ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo(i));
}
}
}
}

/**
Callback for when you get a response from other nodes

@param response The response string received from another node in the mesh
@param meshInstance The ESP8266WiFiMesh instance that called the function.
@returns The status code resulting from the response, as an int
*/
transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance) {
transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE;

/* Print out received message */
Serial.print("Request sent: ");
Serial.println(meshInstance.getMessage());
Serial.print("Response received: ");
Serial.println(response);

// Our last request got a response, so time to create a new request.
meshInstance.setMessage("Hello world request #" + String(++requestNumber) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + ".");

// (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else.
return statusCode;
}

void setup() {
// Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
// This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
WiFi.persistent(false);

Serial.begin(115200);
delay(10);
delay(50); // Wait for Serial.

//yield(); // Use this if you don't want to wait for Serial.

Serial.println();
Serial.println();

Serial.println("Note that this library can use static IP:s for the nodes to speed up connection times.\n"
"Use the setStaticIP method as shown in this example to enable this.\n"
"Ensure that nodes connecting to the same AP have distinct static IP:s.\n"
"Also, remember to change the default mesh network password!\n\n");

Serial.println("Setting up mesh node...");

/* Initialise the mesh node */
mesh_node.begin();
meshNode.begin();
meshNode.activateAP(); // Each AP requires a separate server port.
meshNode.setStaticIP(IPAddress(192, 168, 4, 22)); // Activate static IP mode to speed up connection times.
}

int32_t timeOfLastScan = -10000;
void loop() {
/* Accept any incoming connections */
mesh_node.acceptRequest();

/* Scan for other nodes and send them a message */
char request[60];
sprintf(request, "Hello world request #%d from Mesh_Node%d.", request_i++, ESP.getChipId());
mesh_node.attemptScan(request);
delay(1000);
if (millis() - timeOfLastScan > 3000 // Give other nodes some time to connect between data transfers.
|| (WiFi.status() != WL_CONNECTED && millis() - timeOfLastScan > 2000)) { // Scan for networks with two second intervals when not already connected.
String request = "Hello world request #" + String(requestNumber) + " from " + meshNode.getMeshName() + meshNode.getNodeID() + ".";
meshNode.attemptTransmission(request, false);
timeOfLastScan = millis();

if (ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) {
Serial.println("No mesh AP found.");
} else {
for (TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) {
if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) {
Serial.println("Transmission failed to mesh AP " + transmissionResult.SSID);
} else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) {
Serial.println("Connection failed to mesh AP " + transmissionResult.SSID);
} else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) {
// No need to do anything, transmission was successful.
} else {
Serial.println("Invalid transmission status for " + transmissionResult.SSID + "!");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be an assert, because if the condition ever happens it's due to a programming error or mem corruption.

}
}
}
Serial.println();
} else {
/* Accept any incoming connections */
meshNode.acceptRequest();
}
}
36 changes: 35 additions & 1 deletion libraries/ESP8266WiFiMesh/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,45 @@ ESP8266WiFiMesh KEYWORD3
#######################################

ESP8266WiFiMesh KEYWORD1
NetworkInfo KEYWORD1
TransmissionResult KEYWORD1
transmission_status_t KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

connectionQueue KEYWORD2
latestTransmissionOutcomes KEYWORD2
begin KEYWORD2
attemptScan KEYWORD2
activateAP KEYWORD2
deactivateAP KEYWORD2
restartAP KEYWORD2
getAPController KEYWORD2
isAPController KEYWORD2
getWiFiChannel KEYWORD2
setWiFiChannel KEYWORD2
getMeshName KEYWORD2
setMeshName KEYWORD2
getNodeID KEYWORD2
setNodeID KEYWORD2
setSSID KEYWORD2
getMessage KEYWORD2
setMessage KEYWORD2
attemptTransmission KEYWORD2
acceptRequest KEYWORD2
setStaticIP KEYWORD2
getStaticIP KEYWORD2
disableStaticIP->KEYWORD2
uint64ToString KEYWORD2
stringToUint64 KEYWORD2
getNetworkFilter KEYWORD2
setNetworkFilter KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################

emptyIP LITERAL1
NETWORK_INFO_DEFAULT_INT LITERAL1
WIFI_MESH_EMPTY_STRING LITERAL1
4 changes: 2 additions & 2 deletions libraries/ESP8266WiFiMesh/library.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name=ESP8266WiFiMesh
version=1.0
version=2.0
author=Julian Fell
maintainer=
maintainer=Anders Löfgren
sentence=Mesh network library
paragraph=The library sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes.
category=Communication
Expand Down
Loading