Sarah Marsh / ESP8266Interface

Fork of ESP8266Interface by ESP8266

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ESP8266.cpp Source File

ESP8266.cpp

00001 /* Copyright (C) 2012 mbed.org, MIT License
00002  *
00003  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00005  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00006  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00007  * furnished to do so, subject to the following conditions:
00008  *
00009  * The above copyright notice and this permission notice shall be included in all copies or
00010  * substantial portions of the Software.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  */
00018 
00019 #include "mbed.h"
00020 #include "ESP8266.h"
00021 #include "Endpoint.h"
00022 #include <string>
00023 #include <algorithm>
00024 
00025 //Debug is disabled by default
00026 #if 1
00027 #define DBG(x, ...)  printf("[ESP8266 : DBG]"x"\r\n", ##__VA_ARGS__);
00028 #define WARN(x, ...) printf("[ESP8266 : WARN]"x"\r\n", ##__VA_ARGS__);
00029 #define ERR(x, ...)  printf("[ESP8266 : ERR]"x"\r\n", ##__VA_ARGS__);
00030 #else
00031 #define DBG(x, ...)
00032 #define WARN(x, ...)
00033 #define ERR(x, ...)
00034 #endif
00035 
00036 #ifdef DEBUG
00037 #define INFO(x, ...) printf("[ESP8266 : INFO]"x"\r\n", ##__VA_ARGS__);
00038 #else
00039 #define INFO(x, ...)
00040 #endif
00041 
00042 #define ESP_MAX_TRY_JOIN 3
00043 #define ESP_MAXID 4 // the largest possible ID Value (max num of sockets possible)
00044 
00045 extern Serial pc;
00046 
00047 ESP8266 * ESP8266::inst;
00048 char* ip = NULL;
00049 
00050 ESP8266::ESP8266(   PinName tx, PinName rx, PinName _reset, const char * ssid, const char * phrase, uint32_t baud):
00051     wifi(tx, rx), reset_pin(_reset), buf_ESP8266(256)
00052 {
00053     memset(&state, 0, sizeof(state));
00054 
00055     // change all ' ' in '$' in the ssid and the passphrase
00056     strcpy(this->ssid, ssid);
00057     for (int i = 0; i < strlen(ssid); i++) {
00058         if (this->ssid[i] == ' ')
00059             this->ssid[i] = '$';
00060     }
00061     strcpy(this->phrase, phrase);
00062     for (int i = 0; i < strlen(phrase); i++) {
00063         if (this->phrase[i] == ' ')
00064             this->phrase[i] = '$';
00065     }
00066 
00067 
00068     inst = this;
00069     attach_rx(false);
00070 
00071     wifi.baud(baud); // initial baud rate of the ESP8266
00072 
00073     state.associated = false;
00074     state.cmdMode = false;
00075 }
00076 
00077 bool ESP8266::join()
00078 {
00079     sendCommand( "AT+CWMODE=1", "change", NULL, 1000);
00080     string cmd="AT+CWJAP=\""+(string)this->ssid+"\",\""+(string)this->phrase+"\"";
00081     if( sendCommand( cmd.c_str(), "OK", NULL, 10000) ) {
00082         // successfully joined the network
00083         state.associated = true;
00084         INFO("\r\nssid: %s\r\nphrase: %s\r\nsecurity: %s\r\n\r\n", this->ssid, this->phrase);
00085         return true;
00086     }
00087     return false;
00088 }
00089 
00090 bool ESP8266::connect()
00091 {
00092     sendCommand("AT+CWDHCP=1,1","OK",NULL,1000); // DHCP Enabled in Station Mode
00093     return ESP8266::join();
00094 }
00095 
00096 bool ESP8266::is_connected()
00097 {
00098     return true;
00099 }
00100 
00101 bool ESP8266::start(bool type,char* ip, int port, int id)
00102 {
00103     // Error Check
00104     if(id > ESP_MAXID) {
00105         ERR("startUDPMulti: max id is: %d, id given is %d",ESP_MAXID,id);
00106         return false;
00107     }
00108     // Single Connection Mode
00109     if(id < 0) {
00110         DBG("Start Single Connection Mode");
00111         char portstr[5];
00112         char idstr[2];
00113         bool check [3] = {0};
00114         sprintf(idstr,"%d",id);
00115         sprintf(portstr, "%d", port);
00116         switch(type) {
00117             case ESP_UDP_TYPE : //UDP
00118                 check[0] = sendCommand(( "AT+CIPSTART=\"UDP\",\"" + (string) ip + "\"," + (string) portstr ).c_str(), "OK", NULL, 10000);
00119                 break;
00120             case ESP_TCP_TYPE : //TCP
00121                 check[0] = sendCommand(( "AT+CIPSTART=\"TCP\",\"" + (string) ip + "\"," + (string) portstr ).c_str(), "OK", NULL, 10000);
00122                 break;
00123             default:
00124                 ERR("Default hit for starting connection, this shouldnt be possible!!");
00125                 break;
00126         }
00127         check[1] = sendCommand("AT+CIPMODE=1", "OK", NULL, 1000);// go into transparent mode
00128         check[2] = sendCommand("AT+CIPSEND", ">", NULL, 1000);// go into transparent mode
00129         // check that all commands were sucessful
00130         if(check[0] and check[1] and check[2]) {
00131             DBG("Data Mode\r\n");
00132             state.cmdMode = false;
00133             return true;
00134         } else {
00135             DBG("\r\nstartUDPTransparent Failed for ip:%s, port:%d\r\n",ip,port);
00136             return false;
00137         }
00138     }
00139     // Multi Connection Mode
00140     else {
00141         //TODO: impliment Multi Connection Mode
00142         ERR("Not currently Supported!");
00143         return false;
00144         
00145 //        DBG("Start Multi Connection Mode");
00146 //        char portstr[5];
00147 //        char idstr[2];
00148 //        bool check [3] = {0};
00149 //        sprintf(idstr,"%d",id);
00150 //        sprintf(portstr, "%d", port);
00151 //        switch(type) {
00152 //            case ESP_UDP_TYPE : //UDP
00153 //                check[0] = sendCommand(( "AT+CIPSTART=" + (string) idstr + ",\"UDP\",\"" + (string) ip + "\"," + (string) portstr ).c_str(), "OK", NULL, 10000);
00154 //                break;
00155 //            case ESP_TCP_TYPE : //TCP
00156 //                check[0] = sendCommand(( "AT+CIPSTART=" + (string) idstr + ",\"TCP\",\"" + (string) ip + "\"," + (string) portstr ).c_str(), "OK", NULL, 10000);
00157 //                break;
00158 //            default:
00159 //                ERR("Default hit for starting connection, this shouldnt be possible!!");
00160 //                break;
00161 //        }
00162     }
00163 }
00164 
00165 bool ESP8266::startUDP(char* ip, int port, int id, int length)
00166 {
00167     char portstr[5];
00168     char idstr[1];
00169     char lenstr[2];
00170     
00171     sprintf(portstr, "%d", port);
00172     sprintf(idstr, "%d", id);
00173     sprintf(lenstr, "%d", length);
00174     
00175     sendCommand("AT+CIPMUX=1", "OK", NULL, 1000);
00176     sendCommand(( "AT+CIPSTART=" + string(idstr) + ",\"UDP\",\"" + (string) ip + "\"," + (string) portstr + ",1112,0").c_str(), "OK", NULL, 10000);
00177     sendCommand(("AT+CIPSEND=" + (string)idstr + "," +  (string)lenstr).c_str(), ">", NULL, 1000);// go into transparent mode
00178     DBG("Data Mode\r\n");
00179     state.cmdMode = false;
00180 
00181     return true;
00182 }
00183 
00184 bool ESP8266::startTCPServer(int port)
00185 {
00186     bool command_results[3];
00187     command_results[0]=sendCommand("AT+CWMODE=3", "OK", NULL, 1000);
00188     command_results[1]=sendCommand("AT+CIPMUX=1", "OK", NULL, 1000);
00189     if(port == 333){
00190         command_results[2]=sendCommand("AT+CIPSERVER=1", "OK", NULL, 1000);
00191     }
00192     else{
00193         char portstr[5];
00194         sprintf(portstr, "%d", port);
00195         command_results[2]=sendCommand(("AT+CIPSERVER=1," + (string)portstr).c_str(), "OK", NULL, 1000);
00196     }
00197     //sendCommand("AT+CIFSR", "OK", NULL, 1000);
00198     DBG("Data Mode\r\n");
00199     state.cmdMode = false;
00200     if (command_results[0] and command_results[1] and command_results[2]){
00201         return true;
00202     }
00203     else{
00204         return false;
00205     }
00206 }
00207 
00208 bool ESP8266::close()
00209 {
00210     send("+++",3);
00211     wait(1);
00212     state.cmdMode = true;
00213     sendCommand("AT+CIPCLOSE","OK", NULL, 10000);
00214     return true;
00215 }
00216 
00217 bool ESP8266::disconnect()
00218 {
00219     // if already disconnected, return
00220     if (!state.associated)
00221         return true;
00222     // send command to quit AP
00223     sendCommand("AT+CWQAP", "OK", NULL, 10000);
00224     state.associated = false;
00225     return true;
00226 }
00227 
00228 /*
00229     Assuming Returned data looks like this:
00230     +CIFSR:STAIP,"192.168.11.2"
00231     +CIFSR:STAMAC,"18:fe:34:9f:3a:f5"
00232     grabbing IP from first set of quotation marks
00233 */
00234 char* ESP8266::getIPAddress()
00235 {
00236     char result[30] = {0};
00237     int check = 0;
00238     check = sendCommand("AT+CIFSR", NULL, result, 1000);
00239     //pc.printf("\r\nReceivedInfo for IP Command is: %s\r\n",result);
00240     ip = ipString;
00241     if(check) {
00242         // Success
00243         string resultString(result);
00244         uint8_t pos1 = 0, pos2 = 0;
00245         //uint8_t pos3 = 0, pos4 = 0;
00246         pos1 = resultString.find("+CIFSR:STAIP");
00247         pos1 = resultString.find('"',pos1);
00248         pos2 = resultString.find('"',pos1+1);
00249         //pos3 = resultString.find('"',pos2+1); //would find mac address
00250         //pos4 = resultString.find('"',pos3+1);
00251         strncpy(ipString,resultString.substr(pos1,pos2).c_str(),sizeof(ipString));
00252         ipString[pos2 - pos1 +1] = 0; // null terminate string correctly.
00253         DBG("IP: %s\r\n",ipString);
00254         ip = ipString;
00255 
00256     } else {
00257         // Failure
00258         DBG("getIPAddress() failed\r\n");
00259         ip = NULL;
00260     }
00261     return ip;
00262 }
00263 
00264 bool ESP8266::gethostbyname(const char * host, char * ip)
00265 {
00266     string h = host;
00267     int nb_digits = 0;
00268 
00269     // no dns needed
00270     int pos = h.find(".");
00271     if (pos != string::npos) {
00272         string sub = h.substr(0, h.find("."));
00273         nb_digits = atoi(sub.c_str());
00274     }
00275     //printf("substrL %s\r\n", sub.c_str());
00276     if (count(h.begin(), h.end(), '.') == 3 && nb_digits > 0) {
00277         strcpy(ip, host);
00278         return true;
00279     } else {
00280         // dns needed, not currently available
00281         ERR("gethostbyname(): DNS Not currently available, only use IP Addresses!");
00282         return false;
00283     }
00284 }
00285 
00286 void ESP8266::reset()
00287 {
00288     reset_pin = 0;
00289     wait(0.2);
00290     reset_pin = 1;
00291     wait(1);
00292 
00293     //send("+++",3);
00294     //wait(1);
00295     state.cmdMode = true;
00296     sendCommand("AT", "OK", NULL, 1000);
00297     sendCommand("AT+RST", "ready", NULL, 10000);
00298     state.associated = false;
00299 
00300 }
00301 
00302 bool ESP8266::reboot()
00303 {
00304     reset();
00305     return true;
00306 }
00307 
00308 void ESP8266::handler_rx(void)
00309 {
00310     //read characters
00311     char c;
00312     while (wifi.readable()) {
00313         c=wifi.getc();
00314         buf_ESP8266.queue(c);
00315         //if (state.cmdMode) pc.printf("%c",c); //debug echo, needs fast serial console to prevent UART overruns
00316     }
00317 }
00318 
00319 void ESP8266::attach_rx(bool callback)
00320 {
00321     if (!callback)
00322         wifi.attach(NULL);
00323     else
00324         wifi.attach(this, &ESP8266::handler_rx);
00325 }
00326 
00327 int ESP8266::readable()
00328 {
00329     return buf_ESP8266.available();
00330 }
00331 
00332 int ESP8266::writeable()
00333 {
00334     return wifi.writeable();
00335 }
00336 
00337 char ESP8266::getc()
00338 {
00339     char c=0;
00340     while (!buf_ESP8266.available());
00341     buf_ESP8266.dequeue(&c);
00342     return c;
00343 }
00344 
00345 int ESP8266::putc(char c)
00346 {
00347     while (!wifi.writeable() || wifi.readable()); //wait for echoed command characters to be read first
00348     return wifi.putc(c);
00349 }
00350 
00351 void ESP8266::flush()
00352 {
00353     buf_ESP8266.flush();
00354 }
00355 
00356 int ESP8266::send(const char * buf, int len)
00357 {
00358     //TODO: need to add handler for data > 2048B, this is the max packet size of the ESP8266.
00359     const char* bufptr=buf;
00360     for(int i=0; i<len; i++) {
00361         putc((int)*bufptr++);
00362     }
00363     return len;
00364 }
00365 
00366 bool ESP8266::sendCommand(const char * cmd, const char * ACK, char * res, int timeout)
00367 {
00368     char read;
00369     size_t found = string::npos;
00370     string checking;
00371     Timer tmr;
00372     int result = 0;
00373 
00374     DBG("will send: %s\r\n",cmd);
00375 
00376     attach_rx(true);
00377 
00378     //We flush the buffer
00379     while (readable())
00380         getc();
00381 
00382     if (!ACK || !strcmp(ACK, "NO")) {
00383         for (int i = 0; i < strlen(cmd); i++) {
00384             result = (putc(cmd[i]) == cmd[i]) ? result + 1 : result;
00385             wait(.005); // prevents stuck recieve ready (?) need to let echoed character get read first
00386         }
00387         putc(13); //CR
00388         wait(.005); // wait for echo
00389         putc(10); //LF
00390 
00391     } else {
00392         //We flush the buffer
00393         while (readable())
00394             getc();
00395 
00396         tmr.start();
00397         for (int i = 0; i < strlen(cmd); i++) {
00398             result = (putc(cmd[i]) == cmd[i]) ? result + 1 : result;
00399             wait(.005); // wait for echo
00400         }
00401         putc(13); //CR
00402         wait(.005); // wait for echo
00403         putc(10); //LF
00404 
00405         while (1) {
00406             if (tmr.read_ms() > timeout) {
00407                 //We flush the buffer
00408                 while (readable())
00409                     getc();
00410 
00411                 DBG("check: %s\r\n", checking.c_str());
00412 
00413                 attach_rx(true);
00414                 return -1;
00415             } else if (readable()) {
00416                 read = getc();
00417                 printf("%c",read); //debug echo
00418                 if ( read != '\r' && read != '\n') {
00419                     checking += read;
00420                     found = checking.find(ACK);
00421                     if (found != string::npos) {
00422                         wait(0.01);
00423 
00424                         //We flush the buffer
00425                         while (readable())
00426                             read = getc();
00427                         printf("%c",read); //debug echo
00428                         break;
00429                     }
00430                 }
00431             }
00432         }
00433         DBG("check: %s\r\n", checking.c_str());
00434 
00435         attach_rx(true);
00436         return result;
00437     }
00438 
00439     //the user wants the result from the command (ACK == NULL, res != NULL)
00440     if ( res != NULL) {
00441         int i = 0;
00442         Timer timeout;
00443         timeout.start();
00444         tmr.reset();
00445         while (1) {
00446             if (timeout.read() > 2) {
00447                 if (i == 0) {
00448                     res = NULL;
00449                     break;
00450                 }
00451                 res[i] = '\0';
00452                 DBG("user str 1: %s\r\n", res);
00453 
00454                 break;
00455             } else {
00456                 if (tmr.read_ms() > 300) {
00457                     res[i] = '\0';
00458                     DBG("user str: %s\r\n", res);
00459 
00460                     break;
00461                 }
00462                 if (readable()) {
00463                     tmr.start();
00464                     read = getc();
00465 
00466                     // we drop \r and \n
00467                     if ( read != '\r' && read != '\n') {
00468                         res[i++] = read;
00469                     }
00470                 }
00471             }
00472         }
00473         DBG("user str: %s\r\n", res);
00474     }
00475 
00476     //We flush the buffer
00477     while (readable())
00478         getc();
00479 
00480     attach_rx(true);
00481     DBG("result: %d\r\n", result)
00482     return result;
00483 }