Repository: ekstrand/ESP8266wifi Branch: master Commit: 0bf32ed60bb2 Files: 8 Total size: 41.3 KB Directory structure: gitextract_vuf228nw/ ├── .gitignore ├── LICENSE ├── README.md ├── SerialESP8266wifi.cpp ├── SerialESP8266wifi.h ├── examples/ │ ├── SerialESP8266_library_test/ │ │ └── SerialESP8266_library_test.ino │ └── SerialESP8266_tcp_cli/ │ └── SerialESP8266_tcp_cli.ino └── library.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 ekstrand 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. ================================================ FILE: README.md ================================================ # SerialESP8266wifi A simple ESP8266 Arduino library with built in re-connect functionality. * The ESP8266 is a dirtcheap wifimodule. I got mine for about 2.50 US including shipping at Aliexpress. Read about it here: https://nurdspace.nl/ESP8266 * An AT command reference can be found here: https://github.com/espressif/esp8266_at/wiki/AT_Description * Contact me if you have ideas for changes or new features, found a bug or just want to buy a beer at jonas[AT]inspirativ[DOT]se ## Memory footprint and more * Tested on an Arduino Nano v3 ATMega 328, Arduino IDE 1.60, ESP8266 module with firmware version 0.9.2.4 * approx 3.5kB of program storage * approx 285 bytes or RAM ## Install * Download the library as a zip from https://github.com/ekstrand/SerialESP8266wifi/archive/master.zip * Unzip and place in ARDUINO_HOME/libraries/ directory as SerialESP8266wifi * Restart the Arduino IDE * In your sketch do a `#include ` * To set up a simple server for testing, I like to use SocketTest http://sourceforge.net/projects/sockettest/ ## Constructor **SerialESP8266wifi(Stream serialIn, Stream serialOut, byte resetPin)** * **serialIn** this object is used to read from the ESP8266, you can use either hardware or software serial * **serialOut** this object is used to write to the ESP8266, you can use either hardware or software serial * **resetPin** this pin will be pulled low then high to reset the ESP8266. It is assumed that a the CH_PD pin is connected the this pin. See pin out and more at: http://www.electrodragon.com/w/ESP8266#Module_Pin_Description * **Example:** ```SerialESP8266wifi wifi(swSerial, swSerial, 10);``` **SerialESP8266wifi(Stream serialIn, Stream serialOut, byte resetPin, Stream debugSerial)** * **serialIn** this object is used to read from the ESP8266, you can use either hardware or software serial * **serialOut** this object is used to write to the ESP8266, you can use either hardware or software serial * **resetPin** this pin will be pulled low then high to reset the ESP8266. It is assumed that a the CH_PD pin is connected the this pin. See pin out and more at: http://www.electrodragon.com/w/ ESP8266#Module_Pin_Description * **debugSerial** enables wifi debug and local echo to Serial (could be hw or sw) * **Example:** ```SerialESP8266wifi wifi(swSerial, swSerial, 10, Serial);``` ## Starting the module **boolean begin()** calling this method will do a hw reset on the ESP8266 and set basic parameters * **return** will return a true or false depending if the module was properly initiated * **Example:** `boolean esp8266started = wifi.begin();` ## Connecting to an access point **boolean connectToAP(char * ssid, char* password)** tells the ESP8266 to connect to an accesspoint * **ssid** the ssid (station name) to be used. Note that this method uses char arrays as input. See http://arduino.cc/en/Reference/StringToCharArray for how to convert an arduino string object to a char array (max 15 chars) * **password** the access point password wpa/wpa2 is assumed (max 15 chars) * **return** will return a true if a valid IP was received within the time limit (15 seconds) * **Example:** `boolean apConnected = wifi.connectToAP("myaccesspoint", "password123");` **boolean isConnectedToAP()** checks if the module is connected with a valid IP * **return** will return a true if the module has an valid IP address * **Example:** `boolean apConnected = wifi.isConnectedToAP();` ## Connecting to a server **boolean connectToServer(char* ip, char* port)** tells the ESP8266 to open a connection to a server * **ip** the IP-address of the server to connect to * **port** the port number to be used * **return** true if connection is established within 5 seconds * **Example:** `boolean serverConnected = wifi.connectToServer("192.168.5.123", "2121");` **boolean isConnectedToServer()** checks if a server is connected * **return** will return a true if we are connected to a server * **Example:** `boolean serverConnected = wifi.isConnectedToServer();` **setTransportToTCP() AND setTransportToUDP()** tells the ESP8266 which transport to use when connecting to a server. Default is TCP. ## Disconnecting from a server **disconnectFromServer()** tells the ESP8266 to close the server connection * **Example:** `wifi.disconnectFromServer();` ## Sending a message **boolean send(char channel, char * message)** sends a message - alias for send(char channel, char * message, true) * **channel** Set to **SERVER** if you want to send to server. If we are the server, the value can be between '1'-'3' * **message** a character array, max 25 characters long. * **return** true if the message was sent * **Example:** `boolean sendOk = wifi.send(SERVER, "Hello World!");` **boolean send(char channel, char * message, boolean sendNow)** sends or queues a message for later sending * **channel** Set to **SERVER** if you want to send to server. If we are the server, the value can be between '1'-'3' * **message** a character array, max 25 characters long. * **sendNow** if false, the message is appended to a buffer, if true the message is sent right away * **return** true if the message was sent * **Example:** ``` wifi.send(SERVER, "You", false); wifi.send(SERVER, " are ", false); wifi.send(SERVER, "fantastic!", true); // ie wifi.send(SERVER, "fantastic!"); ``` **endSendWithNewline(bool endSendWithNewline)** by default all messages are sent with newline and carrage return (println), you can disable this * **endSendWithNewline** sent messages with print instead of println * **Example:** `wifi.endSendWithNewline(false);` ## Checking Client Connections **boolean checkConnections(&connections)** - Updates pre-initialised pointer to WifiConnection \*connections. * **return** true if client is connected * Updated pointer is array of 3 connections: * **boolean connected** true if connected. * **char channel** channel number, can be passed to `send`. * **Example:** ``` WifiConnection *connections; wifi.checkConnections(&connections); for (int i = 0; i < MAX_CONNECTIONS; i++) { if (connections[i].connected) { // See if there is a message WifiMessage msg = wifi.getIncomingMessage(); // Check message is there if (msg.hasData) { processCommand(msg); } } } ``` ## Check Connection **boolean isConnection(void)** - Returns true if client is connected, otherwise false. Use as above without WifiConnection pointer if not bothered about multi-client. ## Get Incoming Message From Connected Client **WifiMessage getIncomingMessage(void)** - checks serial buffer for messages. Return is WifiMessage type as below. See example Check Client Connection example for usage. ## Receiving messages **WifiMessage listenForIncomingMessage(int timeoutMillis)** will listen for new messages up to timeoutMillis milliseconds. Call this method as often as possible and with as large timeoutMillis as possible to be able to catch as many messages as possible.. * **timeoutMillis** the maximum number of milliseconds to look for a new incoming message * **return** WifiMessage contains: * **boolean hasData** true if a message was received * **char channel** tells you if the message was received from the server (channel == SERVER) or another source * **char * message** the message as a character array (up to the first 25 characters) * **Example:** ``` void loop(){ WifiMessage in = wifi.listenForIncomingMessage(6000); if (in.hasData) { Serial.print("Incoming message:"); Serial.println(in.message); if(in.channel == SERVER) Serial.println("From server"); else{ Serial.print("From channel:"); Serial.println(in.channel); } } // Do other stuff } ``` ## Local access point and local server **boolean startLocalAPAndServer(char* ssid, char* password, char* channel, char* port)** will create an local access point and start a local server * **ssid** the name for your access point, max 15 characters * **password** the password for your access point, max 15 characters * **channel** the channel for your access point * **port** the port for your local server, TCP only * **return** true if the local access point and server was configured and started * **Example:** `boolean localAPAndServerStarted = wifi.startLocalAPAndServer("my_ap", "secret_pwd", "5", "2121");` **boolean stopLocalAPAndServer()** disable the accesspoint (the server will not be stopped, since a restart is needed) * **return** true if the local access point was stopped * **Example:** `boolean localAPAndServerStopped = wifi.stopLocalAPAndServer();` **boolean isLocalAPAndServerRunning()** check if local access point and server is running * **return** true if the local access point and local server is running * **Example:** `boolean localAPAndServerRunning = wifi.isLocalAPAndServerRunning();` ## Re-connect functionality Everytime send(...) and listenForIncomingMessage(..) is called a watchdog checks that the configured access point, server and local access point and server is still running, if not they will be restarted or re-connected. The same thing happens if the ESP8266 should reset. Note: It is really only the send method that can detect a lost connection to the server. To be sure you are connected, do a send once in a while.. ## Avanced configuration In SerialESP8266wifi.h you can change some stuff: * **HW_RESET_RETRIES 3** - is the maximum number of times begin() will try to start the ESP8266 module * **SERVER_CONNECT_RETRIES_BEFORE_HW_RESET 30** - is the nr of time the watchdog will try to establish connection to a server before a hardware reset of the ESP8266 is performed * The maximum number of characters for incoming and outgoing messages can be changes by editing: * char msgOut[26]; * char msgIn[26]; * If the limit for ssid and password length does not suite you, please change: * char _ssid[16]; * char _password[16]; * char _localAPSSID[16]; * char _localAPPassword[16]; ================================================ FILE: SerialESP8266wifi.cpp ================================================ // // Serialesp8266wifi.cpp // // // Created by Jonas Ekstrand on 2015-02-20. // // #include "SerialESP8266wifi.h" #include "Arduino.h" // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 #ifdef PROGMEM #undef PROGMEM #define PROGMEM __attribute__((section(".progmem.data"))) #endif const char OK[] PROGMEM = "OK"; const char FAIL[] PROGMEM = "FAIL"; const char ERROR[] PROGMEM = "ERROR"; const char NO_CHANGE[] PROGMEM = "no change"; const char SEND_OK[] PROGMEM = "SEND OK"; const char LINK_IS_NOT[] PROGMEM = "link is not"; const char PROMPT[] PROGMEM = ">"; const char BUSY[] PROGMEM = "busy"; const char LINKED[] PROGMEM = "Linked"; const char ALREADY[] PROGMEM = "ALREAY";//yes typo in firmware.. const char READY[] PROGMEM = "ready"; const char NO_IP[] PROGMEM = "0.0.0.0"; const char CIPSEND[] PROGMEM = "AT+CIPSEND="; const char CIPSERVERSTART[] PROGMEM = "AT+CIPSERVER=1,"; const char CIPSERVERSTOP[] PROGMEM = "AT+CIPSERVER=0"; const char CIPSTART[] PROGMEM = "AT+CIPSTART=4,\""; const char CIPCLOSE[] PROGMEM = "AT+CIPCLOSE=4"; const char TCP[] PROGMEM = "TCP"; const char UDP[] PROGMEM = "UDP"; const char CWJAP[] PROGMEM = "AT+CWJAP=\""; const char CWMODE_1[] PROGMEM = "AT+CWMODE=1"; const char CWMODE_3[] PROGMEM = "AT+CWMODE=3"; const char CWMODE_CHECK[] PROGMEM = "AT+CWMODE?"; const char CWMODE_OK[] PROGMEM = "+CWMODE:1"; const char CIFSR[] PROGMEM = "AT+CIFSR"; const char CIPMUX_1[] PROGMEM = "AT+CIPMUX=1"; const char ATE0[] PROGMEM = "ATE0"; const char ATE1[] PROGMEM = "ATE1"; const char CWSAP[] PROGMEM = "AT+CWSAP=\""; const char IPD[] PROGMEM = "IPD,"; const char CONNECT[] PROGMEM = "CONNECT"; const char CLOSED[] PROGMEM = "CLOSED"; const char COMMA[] PROGMEM = ","; const char COMMA_1[] PROGMEM = "\","; const char COMMA_2[] PROGMEM = "\",\""; const char THREE_COMMA[] PROGMEM = ",3"; const char DOUBLE_QUOTE[] PROGMEM = "\""; const char EOL[] PROGMEM = "\n"; const char STAIP[] PROGMEM = "STAIP,\""; const char STAMAC[] PROGMEM = "STAMAC,\""; SerialESP8266wifi::SerialESP8266wifi(Stream &serialIn, Stream &serialOut, byte resetPin) { _serialIn = &serialIn; _serialOut = &serialOut; _resetPin = resetPin; pinMode(_resetPin, OUTPUT); digitalWrite(_resetPin, LOW);//Start with radio off flags.connectToServerUsingTCP = true; flags.endSendWithNewline = true; flags.started = false; flags.localServerConfigured = false; flags.localApConfigured = false; flags.apConfigured = false; flags.serverConfigured = false; flags.debug = false; flags.echoOnOff = false; for (int i = 0; i < MAX_CONNECTIONS; i++) { _connections[i].channel = i + 0x30; // index to ASCII _connections[i].connected = false; } } SerialESP8266wifi::SerialESP8266wifi(Stream &serialIn, Stream &serialOut, byte resetPin, Stream &dbgSerial) { _serialIn = &serialIn; _serialOut = &serialOut; _resetPin = resetPin; pinMode(_resetPin, OUTPUT); digitalWrite(_resetPin, LOW);//Start with radio off flags.connectToServerUsingTCP = true; flags.endSendWithNewline = true; flags.started = false; flags.localServerConfigured = false; flags.localApConfigured = false; flags.apConfigured = false; flags.serverConfigured = false; _dbgSerial = &dbgSerial; flags.debug = true; flags.echoOnOff = true; for (int i = 0; i < MAX_CONNECTIONS; i++) { _connections[i].channel = i + 0x30; // index to ASCII _connections[i].connected = false; } } void niceDelay(int duration){ unsigned long startMillis = millis(); while(millis() - startMillis < duration){ sqrt(4700); } } void SerialESP8266wifi::endSendWithNewline(bool endSendWithNewline){ flags.endSendWithNewline = endSendWithNewline; } bool SerialESP8266wifi::begin() { msgOut[0] = '\0'; msgIn[0] = '\0'; flags.connectedToServer = false; flags.localServerConfigured = false; flags.localApConfigured = false; serverRetries = 0; //Do a HW reset bool statusOk = false; byte i; for(i =0; i 0; return flags.started; } bool SerialESP8266wifi::isStarted(){ return flags.started; } bool SerialESP8266wifi::restart() { return begin() && (!flags.localApConfigured || startLocalAp()) && (!flags.localServerConfigured || startLocalServer()) && (!flags.apConfigured || connectToAP()) && (!flags.serverConfigured || connectToServer()); } bool SerialESP8266wifi::connectToAP(String& ssid, String& password) { return connectToAP(ssid.c_str(), password.c_str()); } bool SerialESP8266wifi::connectToAP(const char* ssid, const char* password){//TODO make timeout config or parameter?? strncpy(_ssid, ssid, sizeof _ssid); strncpy(_password, password, sizeof _password); flags.apConfigured = true; return connectToAP(); } bool SerialESP8266wifi::connectToAP(){ writeCommand(CWJAP); _serialOut -> print(_ssid); writeCommand(COMMA_2); _serialOut -> print(_password); writeCommand(DOUBLE_QUOTE, EOL); readCommand(15000, OK, FAIL); return isConnectedToAP(); } bool SerialESP8266wifi::isConnectedToAP(){ writeCommand(CIFSR, EOL); byte code = readCommand(350, NO_IP, ERROR); readCommand(10, OK); //cleanup return (code == 0); } char* SerialESP8266wifi::getIP(){ msgIn[0] = '\0'; writeCommand(CIFSR, EOL); byte code = readCommand(1000, STAIP, ERROR); if (code == 1) { // found staip readBuffer(&msgIn[0], sizeof(msgIn) - 1, '"'); readCommand(10, OK, ERROR); return &msgIn[0]; } readCommand(1000, OK, ERROR); return &msgIn[0]; } char* SerialESP8266wifi::getMAC(){ msgIn[0] = '\0'; writeCommand(CIFSR, EOL); byte code = readCommand(1000, STAMAC, ERROR); if (code == 1) { // found stamac readBuffer(&msgIn[0], sizeof(msgIn) - 1, '"'); readCommand(10, OK, ERROR); return &msgIn[0]; } readCommand(1000, OK, ERROR); return &msgIn[0]; } void SerialESP8266wifi::setTransportToUDP(){ flags.connectToServerUsingTCP = false; } void SerialESP8266wifi::setTransportToTCP(){ flags.connectToServerUsingTCP = true; } bool SerialESP8266wifi::connectToServer(String& ip, String& port) { return connectToServer(ip.c_str(), port.c_str()); } bool SerialESP8266wifi::connectToServer(const char* ip, const char* port){//TODO make timeout config or parameter?? strncpy(_ip, ip, sizeof _ip); strncpy(_port, port, sizeof _port); flags.serverConfigured = true; return connectToServer(); } bool SerialESP8266wifi::connectToServer(){ writeCommand(CIPSTART); if (flags.connectToServerUsingTCP) writeCommand(TCP); else writeCommand(UDP); writeCommand(COMMA_2); _serialOut -> print(_ip); writeCommand(COMMA_1); _serialOut -> println(_port); flags.connectedToServer = (readCommand(10000, LINKED, ALREADY) > 0); if(flags.connectedToServer) serverRetries = 0; return flags.connectedToServer; } void SerialESP8266wifi::disconnectFromServer(){ flags.connectedToServer = false; flags.serverConfigured = false;//disable reconnect writeCommand(CIPCLOSE); readCommand(2000, OK); //fire and forget in this case.. } bool SerialESP8266wifi::isConnectedToServer(){ if(flags.connectedToServer) serverRetries = 0; return flags.connectedToServer; } bool SerialESP8266wifi::startLocalAPAndServer(const char* ssid, const char* password, const char* channel, const char* port){ strncpy(_localAPSSID, ssid, sizeof _localAPSSID); strncpy(_localAPPassword, password, sizeof _localAPPassword); strncpy(_localAPChannel, channel, sizeof _localAPChannel); strncpy(_localServerPort, port, sizeof _localServerPort); flags.localApConfigured = true; flags.localServerConfigured = true; return startLocalAp() && startLocalServer(); } bool SerialESP8266wifi::startLocalAP(const char* ssid, const char* password, const char* channel){ strncpy(_localAPSSID, ssid, sizeof _localAPSSID); strncpy(_localAPPassword, password, sizeof _localAPPassword); strncpy(_localAPChannel, channel, sizeof _localAPChannel); flags.localApConfigured = true; return startLocalAp(); } bool SerialESP8266wifi::startLocalServer(const char* port) { strncpy(_localServerPort, port, sizeof _localServerPort); flags.localServerConfigured = true; return startLocalServer(); } bool SerialESP8266wifi::startLocalServer(){ // Start local server writeCommand(CIPSERVERSTART); _serialOut -> println(_localServerPort); flags.localServerRunning = (readCommand(2000, OK, NO_CHANGE) > 0); return flags.localServerRunning; } bool SerialESP8266wifi::startLocalAp(){ // Start local ap mode (eg both local ap and ap) writeCommand(CWMODE_3, EOL); if (!readCommand(2000, OK, NO_CHANGE)) return false; // Configure the soft ap writeCommand(CWSAP); _serialOut -> print(_localAPSSID); writeCommand(COMMA_2); _serialOut -> print(_localAPPassword); writeCommand(COMMA_1); _serialOut -> print(_localAPChannel); writeCommand(THREE_COMMA, EOL); flags.localApRunning = (readCommand(5000, OK, ERROR) == 1); return flags.localApRunning; } bool SerialESP8266wifi::stopLocalServer(){ writeCommand(CIPSERVERSTOP, EOL); boolean stopped = (readCommand(2000, OK, NO_CHANGE) > 0); flags.localServerRunning = !stopped; flags.localServerConfigured = false; //to prevent autostart return stopped; } bool SerialESP8266wifi::stopLocalAP(){ writeCommand(CWMODE_1, EOL); boolean stopped = (readCommand(2000, OK, NO_CHANGE) > 0); flags.localApRunning = !stopped; flags.localApConfigured = false; //to prevent autostart return stopped; } bool SerialESP8266wifi::stopLocalAPAndServer(){ return stopLocalAP() && stopLocalServer(); } bool SerialESP8266wifi::isLocalAPAndServerRunning(){ return flags.localApRunning & flags.localServerRunning; } // Performs a connect retry (or hardware reset) if not connected bool SerialESP8266wifi::watchdog() { if (serverRetries >= SERVER_CONNECT_RETRIES_BEFORE_HW_RESET) { // give up, do a hardware reset return restart(); } if (flags.serverConfigured && !flags.connectedToServer) { serverRetries++; if (flags.apConfigured && !isConnectedToAP()) { if (!connectToAP()) { // wait a bit longer, then check again niceDelay(2000); if (!isConnectedToAP()) { return restart(); } } } return connectToServer(); } return true; } /* * Send string (if channel is connected of course) */ bool SerialESP8266wifi::send(char channel, String& message, bool sendNow) { return send(channel, message.c_str(), sendNow); } bool SerialESP8266wifi::send(char channel, const char * message, bool sendNow){ watchdog(); byte avail = sizeof(msgOut) - strlen(msgOut) - 1; strncat(msgOut, message, avail); if (!sendNow) return true; byte length = strlen(msgOut); if(flags.endSendWithNewline) length += 2; writeCommand(CIPSEND); _serialOut -> print(channel); writeCommand(COMMA); _serialOut -> println(length); byte prompt = readCommand(1000, PROMPT, LINK_IS_NOT); if (prompt != 2) { if(flags.endSendWithNewline) _serialOut -> println(msgOut); else _serialOut -> print(msgOut); byte sendStatus = readCommand(5000, SEND_OK, BUSY); if (sendStatus == 1) { msgOut[0] = '\0'; if(channel == SERVER) flags.connectedToServer = true; return true; } } //else if(channel == SERVER) flags.connectedToServer = false; else _connections[channel-0x30].connected = false; msgOut[0] = '\0'; return false; } // Checks to see if there is a client connection bool SerialESP8266wifi::isConnection(void) { WifiConnection *connections; // return the first channel, assume single connection use return checkConnections(&connections); } // Updates private connections struct and make passed pointer point to data bool SerialESP8266wifi::checkConnections(WifiConnection **pConnections) { watchdog(); // setup buffers on stack & copy data from PROGMEM pointers char buf1[16] = {'\0'}; char buf2[16] = {'\0'}; char buf3[16] = {'\0'}; strcpy_P(buf1, CONNECT); strcpy_P(buf2, READY); strcpy_P(buf3, CLOSED); byte len1 = strlen(buf1); byte len2 = strlen(buf2); byte len3 = strlen(buf3); byte pos = 0; byte pos1 = 0; byte pos2 = 0; byte pos3 = 0; byte ret = 0; char ch = '-'; // unload buffer and check match while (_serialIn->available()) { char c = readChar(); // skip white space if (c != ' ') { // get out of here if theres a message if (c == '+') break; // first char is channel if (pos == 0) ch = c; pos++; pos1 = (c == buf1[pos1]) ? pos1 + 1 : 0; pos2 = (c == buf2[pos2]) ? pos2 + 1 : 0; pos3 = (c == buf3[pos3]) ? pos3 + 1 : 0; if (len1 > 0 && pos1 == len1) { ret = 1; break; } if (len2 > 0 && pos2 == len2) { ret = 2; break; } if (len3 > 0 && pos3 == len3) { ret = 3; break; } } } if (ret == 2) restart(); // new connection if (ret == 1) { _connections[ch-0x30].connected = true; *pConnections = _connections; if (ch == SERVER) flags.connectedToServer = true; return 1; } // channel disconnected if (ret == 3) { _connections[ch-0x30].connected = false; *pConnections = _connections; if (ch == SERVER) flags.connectedToServer = false; return 0; } // nothing has changed return single connection status *pConnections = _connections; return _connections[0].connected; } WifiMessage SerialESP8266wifi::listenForIncomingMessage(int timeout){ watchdog(); char buf[16] = {'\0'}; msgIn[0] = '\0'; static WifiMessage msg; msg.hasData = false; msg.channel = '-'; msg.message = msgIn; //TODO listen for unlink etc... byte msgOrRestart = readCommand(timeout, IPD, READY); //Detected a esp8266 restart if (msgOrRestart == 2){ restart(); return msg; } //Message received.. else if (msgOrRestart == 1) { char channel = readChar(); if (channel == SERVER) flags.connectedToServer = true; readChar(); // removing comma readBuffer(&buf[0], sizeof(buf) - 1, ':'); // read char count readChar(); // removing ':' delim byte length = atoi(buf); readBuffer(&msgIn[0], min(length, sizeof(msgIn) - 1)); msg.hasData = true; msg.channel = channel; msg.message = msgIn; readCommand(10, OK); // cleanup after rx } return msg; } WifiMessage SerialESP8266wifi::getIncomingMessage(void) { watchdog(); char buf[16] = {'\0'}; msgIn[0] = '\0'; static WifiMessage msg; msg.hasData = false; msg.channel = '-'; msg.message = msgIn; // See if a message has come in (block 1s otherwise misses?) byte msgOrRestart = readCommand(10, IPD, READY); //Detected a esp8266 restart if (msgOrRestart == 2){ restart(); return msg; } //Message received.. else if (msgOrRestart == 1) { char channel = readChar(); if (channel == SERVER) flags.connectedToServer = true; readChar(); // removing comma readBuffer(&buf[0], sizeof(buf) - 1, ':'); // read char count readChar(); // removing ':' delim byte length = atoi(buf); readBuffer(&msgIn[0], min(length, sizeof(msgIn) - 1)); msg.hasData = true; msg.channel = channel; msg.message = msgIn; readCommand(10, OK); // cleanup after rx } return msg; } // Writes commands (from PROGMEM) to serial output void SerialESP8266wifi::writeCommand(const char* text1 = NULL, const char* text2) { char buf[16] = {'\0'}; strcpy_P(buf, (char *) text1); _serialOut->print(buf); if (text2 == EOL) { _serialOut->println(); } else if (text2 != NULL) { strcpy_P(buf, (char *) text2); _serialOut->print(buf); } } // Reads from serial input until a expected string is found (or until timeout) // NOTE: strings are stored in PROGMEM (auto-copied by this method) byte SerialESP8266wifi::readCommand(int timeout, const char* text1, const char* text2) { // setup buffers on stack & copy data from PROGMEM pointers char buf1[16] = {'\0'}; char buf2[16] = {'\0'}; if (text1 != NULL) strcpy_P(buf1, (char *) text1); if (text2 != NULL) strcpy_P(buf2, (char *) text2); byte len1 = strlen(buf1); byte len2 = strlen(buf2); byte pos1 = 0; byte pos2 = 0; // read chars until first match or timeout unsigned long stop = millis() + timeout; do { while (_serialIn->available()) { char c = readChar(); pos1 = (c == buf1[pos1]) ? pos1 + 1 : 0; pos2 = (c == buf2[pos2]) ? pos2 + 1 : 0; if (len1 > 0 && pos1 == len1) return 1; if (len2 > 0 && pos2 == len2) return 2; } niceDelay(10); } while (millis() < stop); return 0; } // Unload buffer without delay /*byte ESP8266wifiESP8266wifi::readCommand(const char* text1, const char* text2) { // setup buffers on stack & copy data from PROGMEM pointers char buf1[16] = {'\0'}; char buf2[16] = {'\0'}; if (text1 != NULL) strcpy_P(buf1, (char *) text1); if (text2 != NULL) strcpy_P(buf2, (char *) text2); byte len1 = strlen(buf1); byte len2 = strlen(buf2); byte pos1 = 0; byte pos2 = 0; // read chars until first match or timeout while (_serialIn->available()) { char c = readChar(); pos1 = (c == buf1[pos1]) ? pos1 + 1 : 0; pos2 = (c == buf2[pos2]) ? pos2 + 1 : 0; if (len1 > 0 && pos1 == len1) return 1; if (len2 > 0 && pos2 == len2) return 2; } return 0; }*/ // Reads count chars to a buffer, or until delim char is found byte SerialESP8266wifi::readBuffer(char* buf, byte count, char delim) { byte pos = 0; char c; while (_serialIn->available() && pos < count) { c = readChar(); if (c == delim) break; buf[pos++] = c; } buf[pos] = '\0'; return pos; } // Reads a single char from serial input (with debug printout if configured) char SerialESP8266wifi::readChar() { char c = _serialIn->read(); if (flags.debug) _dbgSerial->print(c); else sqrt(12345);//delayMicroseconds(50); // don't know why return c; } ================================================ FILE: SerialESP8266wifi.h ================================================ // // SerialESP8266wifi.h // // // Created by Jonas Ekstrand on 2015-02-20. // ESP8266 AT cmd ref from https://github.com/espressif/esp8266_at/wiki/CIPSERVER // // #ifndef SerialESP8266wifi_h #define SerialESP8266wifi_h #define HW_RESET_RETRIES 3 #define SERVER_CONNECT_RETRIES_BEFORE_HW_RESET 3 #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif #include #if defined(SerialESP8266) #include #else #include #endif #include "HardwareSerial.h" #define SERVER '4' #define MAX_CONNECTIONS 3 #define MSG_BUFFER_MAX 128 struct WifiMessage{ public: bool hasData:1; char channel; char * message; }; struct WifiConnection{ public: char channel; bool connected:1; }; struct Flags // 1 byte value (on a system where 8 bits is a byte { bool started:1, echoOnOff:1, debug:1, serverConfigured:1, // true if a connection to a remote server is configured connectedToServer:1, // true if a connection to a remote server is established apConfigured:1, // true if the module is configured as a client station localApConfigured:1, localServerConfigured:1, localApRunning:1, localServerRunning:1, endSendWithNewline:1, connectToServerUsingTCP:1; }; class SerialESP8266wifi { public: /* * Will pull resetPin low then high to reset esp8266, connect this pin to CHPD pin */ SerialESP8266wifi(Stream &serialIn, Stream &serialOut, byte resetPin); /* * Will pull resetPin low then high to reset esp8266, connect this pin to CHPD pin */ SerialESP8266wifi(Stream &serialIn, Stream &serialOut, byte resetPin, Stream &dbgSerial); /* * Will do hw reset and set inital configuration, will try this HW_RESET_RETRIES times. */ bool begin(); // reset and set echo and other stuff bool isStarted(); /* * Connect to AP using wpa encryption * (reconnect logic is applied, if conn lost or not established, or esp8266 restarted) */ bool connectToAP(String& ssid, String& password); bool connectToAP(const char* ssid, const char* password); bool isConnectedToAP(); char* getIP(); char* getMAC(); /* * Evaluate the connection and perform reconnects if needed. Eventually perform reset and restart. * */ bool watchdog(); /* * Connecting with TCP to server * (reconnect logic is applied, if conn lost or not established, or esp8266 restarted) */ void setTransportToUDP(); //Default.. void setTransportToTCP(); bool connectToServer(String& ip, String& port); bool connectToServer(const char* ip, const char* port); void disconnectFromServer(); bool isConnectedToServer(); /* * Starting local AP and local TCP-server * (reconnect logic is applied, if conn lost or not established, or esp8266 restarted) */ bool startLocalAPAndServer(const char* ssid, const char* password, const char* channel,const char* port); bool startLocalAP(const char* ssid, const char* password, const char* channel); bool startLocalServer(const char* port); bool stopLocalAPAndServer(); bool stopLocalAP(); bool stopLocalServer(); bool isLocalAPAndServerRunning(); /* * Send string (if channel is connected of course) */ bool send(char channel, String& message, bool sendNow = true); bool send(char channel, const char * message, bool sendNow = true); /* * Default is true. */ void endSendWithNewline(bool endSendWithNewline); /* * Scan for incoming message, do this as often and as long as you can (use as sleep in loop) */ WifiMessage listenForIncomingMessage(int timeoutMillis); WifiMessage getIncomingMessage(void); bool isConnection(void); bool checkConnections(WifiConnection **pConnections); private: Stream* _serialIn; Stream* _serialOut; byte _resetPin; Flags flags; bool connectToServer(); char _ip[16]; char _port[6]; bool connectToAP(); char _ssid[16]; char _password[16]; bool startLocalAp(); bool startLocalServer(); char _localAPSSID[16]; char _localAPPassword[16]; char _localAPChannel[3]; char _localServerPort[6]; WifiConnection _connections[MAX_CONNECTIONS]; bool restart(); byte serverRetries; char msgOut[MSG_BUFFER_MAX];//buffer for send method char msgIn[MSG_BUFFER_MAX]; //buffer for listen method = limit of incoming message.. void writeCommand(const char* text1, const char* text2 = NULL); byte readCommand(int timeout, const char* text1 = NULL, const char* text2 = NULL); //byte readCommand(const char* text1, const char* text2); byte readBuffer(char* buf, byte count, char delim = '\0'); char readChar(); Stream* _dbgSerial; }; #endif ================================================ FILE: examples/SerialESP8266_library_test/SerialESP8266_library_test.ino ================================================ #include #include #define sw_serial_rx_pin 4 // Connect this pin to TX on the esp8266 #define sw_serial_tx_pin 6 // Connect this pin to RX on the esp8266 #define esp8266_reset_pin 5 // Connect this pin to CH_PD on the esp8266, not reset. (let reset be unconnected) SoftwareSerial swSerial(sw_serial_rx_pin, sw_serial_tx_pin); // the last parameter sets the local echo option for the ESP8266 module.. SerialESP8266wifi wifi(swSerial, swSerial, esp8266_reset_pin, Serial);//adding Serial enabled local echo and wifi debug String inputString; boolean stringComplete = false; unsigned long nextPing = 0; void setup() { inputString.reserve(20); swSerial.begin(9600); Serial.begin(9600); while (!Serial) ; Serial.println("Starting wifi"); wifi.setTransportToTCP();// this is also default // wifi.setTransportToUDP();//Will use UDP when connecting to server, default is TCP wifi.endSendWithNewline(true); // Will end all transmissions with a newline and carrage return ie println.. default is true wifi.begin(); //Turn on local ap and server (TCP) wifi.startLocalAPAndServer("MY_CONFIG_AP", "password", "5", "2121"); wifi.connectToAP("wifissid", "wifipass"); wifi.connectToServer("192.168.0.28", "2121"); wifi.send(SERVER, "ESP8266 test app started"); } void loop() { //Make sure the esp8266 is started.. if (!wifi.isStarted()) wifi.begin(); //Send what you typed in the arduino console to the server static char buf[20]; if (stringComplete) { inputString.toCharArray(buf, sizeof buf); wifi.send(SERVER, buf); inputString = ""; stringComplete = false; } //Send a ping once in a while.. if (millis() > nextPing) { wifi.send(SERVER, "Ping ping.."); nextPing = millis() + 10000; } //Listen for incoming messages and echo back, will wait until a message is received, or max 6000ms.. WifiMessage in = wifi.listenForIncomingMessage(6000); if (in.hasData) { if (in.channel == SERVER) Serial.println("Message from the server:"); else Serial.println("Message a local client:"); Serial.println(in.message); //Echo back; wifi.send(in.channel, "Echo:", false); wifi.send(in.channel, in.message); nextPing = millis() + 10000; } //If you want do disconnect from the server use: // wifi.disconnectFromServer(); } //Listen for serial input from the console void serialEvent() { while (Serial.available()) { char inChar = (char)Serial.read(); inputString += inChar; if (inChar == '\n') { stringComplete = true; } } } ================================================ FILE: examples/SerialESP8266_tcp_cli/SerialESP8266_tcp_cli.ino ================================================ #include #include /* TCP server/client example, that manages client connections, checks for messages * when client is connected and parses commands. Connect to the ESP8266 IP using * a TCP client such as telnet, eg: telnet 192.168.0.X 2121 * * ESP8266 should be AT firmware based on 1.5 SDK or later * * 2016 - J.Whittington - engineer.john-whittington.co.uk */ #define sw_serial_rx_pin 4 // Connect this pin to TX on the esp8266 #define sw_serial_tx_pin 6 // Connect this pin to RX on the esp8266 #define esp8266_reset_pin 5 // Connect this pin to CH_PD on the esp8266, not reset. (let reset be unconnected) #define SERVER_PORT "2121" #define SSID "YourSSID" #define PASSWORD "YourPassword" SoftwareSerial swSerial(sw_serial_rx_pin, sw_serial_tx_pin); // the last parameter sets the local echo option for the ESP8266 module.. SerialESP8266wifi wifi(Serial, Serial, esp8266_reset_pin, swSerial); void processCommand(WifiMessage msg); uint8_t wifi_started = false; // TCP Commands const char RST[] PROGMEM = "RST"; const char IDN[] PROGMEM = "*IDN?"; void setup() { // start debug serial swSerial.begin(9600); // start HW serial for ESP8266 (change baud depending on firmware) Serial.begin(115200); while (!Serial) ; Serial.println("Starting wifi"); swSerial.println("Starting wifi"); wifi.setTransportToTCP();// this is also default wifi.endSendWithNewline(false); // Will end all transmissions with a newline and carrage return ie println.. default is true wifi_started = wifi.begin(); if (wifi_started) { wifi.connectToAP(SSID, PASSWORD); wifi.startLocalServer(SERVER_PORT); } else { // ESP8266 isn't working.. } } void loop() { static WifiConnection *connections; // check connections if the ESP8266 is there if (wifi_started) wifi.checkConnections(&connections); // check for messages if there is a connection for (int i = 0; i < MAX_CONNECTIONS; i++) { if (connections[i].connected) { // See if there is a message WifiMessage msg = wifi.getIncomingMessage(); // Check message is there if (msg.hasData) { // process the command processCommand(msg); } } } } void processCommand(WifiMessage msg) { // return buffer char espBuf[MSG_BUFFER_MAX]; // scanf holders int set; char str[16]; // Get command and setting sscanf(msg.message,"%15s %d",str,&set); /* swSerial.print(str);*/ /* swSerial.println(set);*/ if ( !strcmp_P(str,IDN) ) { wifi.send(msg.channel,"ESP8266wifi Example"); } // Reset system by temp enable watchdog else if ( !strcmp_P(str,RST) ) { wifi.send(msg.channel,"SYSTEM RESET..."); // soft reset by reseting PC asm volatile (" jmp 0"); } // Unknown command else { wifi.send(msg.channel,"ERR"); } } ================================================ FILE: library.json ================================================ { "name": "SerialESP8266wifi", "keywords": "wifi, wi-fi, http, web, server, client", "description": "ESP8266 Arduino library with built in reconnect functionality", "repository": { "type": "git", "url": "https://github.com/ekstrand/ESP8266wifi.git" }, "frameworks": "arduino", "platforms": "atmelavr" }