/* ESP8266WiFiMesh.h - Mesh network node Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. Copyright (c) 2015 Julian Fell. All rights reserved. Updated 2018 by Anders Löfgren. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __WIFIMESH_H__ #define __WIFIMESH_H__ #include #include #include #include #include "NetworkInfo.h" #include "TransmissionResult.h" #define ENABLE_STATIC_IP_OPTIMIZATION // Requires Arduino core for ESP8266 version 2.4.2 or higher and lwIP2 (lwIP can be changed in "Tools" menu of Arduino IDE). #define ENABLE_WIFI_SCAN_OPTIMIZATION // Requires Arduino core for ESP8266 version 2.4.2 or higher. Scan time should go from about 2100 ms to around 60 ms if channel 1 (standard) is used. const String WIFI_MESH_EMPTY_STRING = ""; class ESP8266WiFiMesh { private: String _SSID; String _meshName; String _nodeID; uint16_t _serverPort; String _meshPassword; uint8 _meshWiFiChannel; bool _verboseMode; WiFiServer _server; uint32_t _lwipVersion[3]; static const uint32_t lwipVersion203Signature[3]; String _message = WIFI_MESH_EMPTY_STRING; bool _scanHidden = false; bool _apHidden = false; uint8_t _maxAPStations = 4; int32_t _connectionAttemptTimeoutMs = 10000; int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. uint32_t _apModeTimeoutMs = 4500; static String lastSSID; static bool staticIPActivated; static IPAddress staticIP; static IPAddress gateway; static IPAddress subnetMask; static ESP8266WiFiMesh *apController; typedef std::function requestHandlerType; typedef std::function responseHandlerType; typedef std::function networkFilterType; requestHandlerType _requestHandler; responseHandlerType _responseHandler; networkFilterType _networkFilter; void updateNetworkNames(const String &newMeshName = WIFI_MESH_EMPTY_STRING, const String &newNodeID = WIFI_MESH_EMPTY_STRING); void verboseModePrint(const String &stringToPrint, bool newline = true); void fullStop(WiFiClient &currClient); void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); transmission_status_t connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); transmission_status_t exchangeInfo(WiFiClient &currClient); bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); transmission_status_t attemptDataTransfer(); transmission_status_t attemptDataTransferKernel(); void storeLwipVersion(); bool atLeastLwipVersion(const uint32_t minLwipVersion[3]); //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// typedef std::function compatibilityLayerHandlerType; String _ssidPrefix; uint32_t _chipID; compatibilityLayerHandlerType _handler = NULL; WiFiClient _client; void connectToNode(const String &targetSSID, const char *message); bool exchangeInfo(const char *message, WiFiClient &currClient); bool waitForClient(WiFiClient &currClient, int maxWait); void attemptScanKernel(const char *message); //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// public: //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// /** * WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. * * @chipID A unique identifier number for the node. * @handler The callback handler for dealing with received messages. Takes a string as an argument which * is the string received from another node and returns the string to send back. * */ ESP8266WiFiMesh(uint32_t chipID, compatibilityLayerHandlerType handler); /** * Scan for other nodes, and exchange the chosen message with any that are found. * * @message The message to send to all other nodes. * */ void attemptScan(const String &message); void attemptScan(char *message); template void attemptScan(char (&message)[Size]); //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// ~ESP8266WiFiMesh(); /** * 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, uint16_t serverPort = 4011); /** * 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 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. * 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 latestTransmissionOutcomes; /** * @returns 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(); /** * Initialises the node. */ void begin(); /** * Each AP requires a separate server port. If two AP:s 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. */ void activateAP(); void deactivateAP(); void restartAP(); /** * Get the ESP8266WiFiMesh instance currently in control of the ESP8266 AP. * Note that the result will be nullptr when there is no active AP controller. * If another instance takes control over the AP after the pointer is created, * the created pointer will still point to the old AP instance. * * @returns A pointer to the ESP8266WiFiMesh instance currently in control of the ESP8266 AP, * or nullptr if there is no active AP controller. */ static ESP8266WiFiMesh * getAPController(); /** * Check if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. * * @returns True if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. False otherwise. */ bool isAPController(); /** * Change the WiFi channel used by this ESP8266WiFiMesh instance. * Will also change the WiFi channel for the active AP if this ESP8266WiFiMesh instance is the current AP controller and it is possible to change channel. * * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. * 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 newWiFiChannel The WiFi channel to change to. Valid values are integers from 1 to 13. * */ void setWiFiChannel(uint8 newWiFiChannel); uint8 getWiFiChannel(); /** * Change the mesh name used by this ESP8266WiFiMesh instance. * Will also change the mesh name (SSID prefix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param newMeshName The mesh name to change to. */ void setMeshName(const String &newMeshName); String getMeshName(); /** * Change the node id used by this ESP8266WiFiMesh instance. * Will also change the node id (SSID suffix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param newNodeID The node id to change to. */ void setNodeID(const String &newNodeID); String getNodeID(); /** * Change the SSID (mesh name + node id) used by this ESP8266WiFiMesh instance. * Will also change the SSID for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param newMeshName The mesh name to change to. Will be the SSID prefix. * @param newNodeID The node id to change to. Will be the SSID suffix. */ void setSSID(const String &newMeshName, const String &newNodeID); String getSSID(); /** * Set the message that will be sent to other nodes when calling attemptTransmission. * * @param newMessage The message to send. */ void setMessage(const String &newMessage); String getMessage(); /** * If AP connection already exists, and the initialDisconnect argument is set to false, send message only to the already connected AP. * Otherwise, scan for other networks, send the scan result to networkFilter and then transmit the message to the networks found in connectionQueue. * * @param message The message to send to other nodes. It will be stored in the class instance until replaced via attemptTransmission or setMessage. * @param concludingDisconnect Disconnect from AP once transmission is complete. * @param initialDisconnect Disconnect from any currently connected AP before attempting transmission. * @param noScan Do not scan for new networks and do not call networkFilter function. Will only use the data already in connectionQueue for the transmission. * @param scanAllWiFiChannels Scan all WiFi channels during a WiFi scan, instead of just the channel the ESP8266WiFiMesh instance is using. * Scanning all WiFi channels takes about 2100 ms, compared to just 60 ms if only channel 1 (standard) is scanned. * 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. * This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. */ void attemptTransmission(const String &message, bool concludingDisconnect = true, bool initialDisconnect = false, bool noScan = false, bool scanAllWiFiChannels = false); /** * If any clients are connected, accept their requests and call the requestHandler function for each one. */ void acceptRequest(); /** * 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. */ void setStaticIP(const IPAddress &newIP); IPAddress getStaticIP(); void disableStaticIP(); /** * An empty IPAddress. Used as default when no IP is set. */ static const IPAddress emptyIP; void setRequestHandler(requestHandlerType requestHandler); requestHandlerType getRequestHandler(); void setResponseHandler(responseHandlerType responseHandler); responseHandlerType getResponseHandler(); void setNetworkFilter(networkFilterType networkFilter); networkFilterType getNetworkFilter(); /** * Set whether scan results from this ESP8266WiFiMesh instance will include WiFi networks with hidden SSIDs. * This is false by default. * The SSID field of a found hidden network will be blank in the scan results. * WiFi.isHidden(networkIndex) can be used to verify that a found network is hidden. * * @param scanHidden If true, WiFi networks with hidden SSIDs will be included in scan results. */ void setScanHidden(bool scanHidden); bool getScanHidden(); /** * Set whether the AP controlled by this ESP8266WiFiMesh instance will have a WiFi network with hidden SSID. * This is false by default. * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param apHidden If true, the WiFi network created will have a hidden SSID. */ void setAPHidden(bool apHidden); bool getAPHidden(); /** * Set the maximum number of stations that can simultaneously be connected to the AP controlled by this ESP8266WiFiMesh instance. * This number is 4 by default. * Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. * The more stations that are connected, the more memory is required. * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param maxAPStations The maximum number of simultaneous station connections allowed. Valid values are 0 to 8. */ void setMaxAPStations(uint8_t maxAPStations); bool getMaxAPStations(); /** * Set the timeout for each attempt to connect to another AP that occurs through the attemptTransmission method by this ESP8266WiFiMesh instance. * The timeout is 10 000 ms by default. * * @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds. */ void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs); int32_t getConnectionAttemptTimeout(); /** * Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as a station (i.e. when connected to another AP). * This will affect the timeout of the attemptTransmission method once a connection to an AP has been established. * The timeout is 5 000 ms by default. * * @param stationModeTimeoutMs The timeout to use, in milliseconds. */ void setStationModeTimeout(int stationModeTimeoutMs); int getStationModeTimeout(); /** * Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as an AP (i.e. when receiving connections from other stations). * This will affect the timeout of the acceptRequest method. * The timeout is 4 500 ms by default. * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. * * @param apModeTimeoutMs The timeout to use, in milliseconds. */ void setAPModeTimeout(uint32_t apModeTimeoutMs); uint32_t getAPModeTimeout(); }; #endif