ESP8266 / ESP8266NodeMCUInterface

Dependencies:   BufferedSerial

Dependents:   esp8266_nodeMCU1 esp8266_2_thingspeak1 Solarator_0-0-2 IoTBurglar_and_Fire_AlarmSystem ... more

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 0
00027 #define DBG(x, ...)  printf("[ESP8266 : DBG]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
00028 #define WARN(x, ...) printf("[ESP8266 : WARN]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
00029 #define ERR(x, ...)  printf("[ESP8266 : ERR]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
00030 #define INFO(x, ...) printf("[ESP8266 : INFO]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
00031 #ifndef ESP8266_ECHO
00032 #define ESP8266_ECHO 1
00033 #endif
00034 #else
00035 #define DBG(x, ...)
00036 #define WARN(x, ...)
00037 #define ERR(x, ...)
00038 #define INFO(x, ...)
00039 #endif
00040 
00041 
00042 ESP8266 *ESP8266::_inst;
00043 
00044 ESP8266::ESP8266(PinName tx, PinName rx, PinName reset, int baud, int timeout) :
00045         _serial(tx, rx), _reset_pin(reset) {
00046     INFO("Initializing ESP8266 object");
00047     
00048     _baud = baud;
00049     _timeout = timeout;
00050     
00051     _serial.baud(_baud);
00052     
00053     _inst = this;
00054 }
00055 
00056 bool ESP8266::reset() {
00057     _reset_pin = 0;
00058     wait_us(20);
00059     _reset_pin = 1;
00060     
00061     // Send reboot command in case reset is not connected
00062     return command("\x03\r\n" "node.restart()") && execute();
00063 }
00064 
00065 bool ESP8266::init() {
00066     // Reset device to clear state
00067     return reset();
00068 }
00069 
00070 bool ESP8266::connect(const char *ssid, const char *phrase) {
00071     // Configure as station with passed ssid and passphrase
00072     if (!(command("wifi.setmode(wifi.STATION);") &&
00073           command("wifi.sta.config('") &&
00074           command(ssid) &&
00075           command("','") &&
00076           command(phrase) &&
00077           command("')") &&
00078           execute()))
00079         return false;
00080         
00081     // Wait for an IP address
00082     // TODO WISHLIST make this a seperate check so it can run asynch?
00083     Timer timer;
00084     timer.start();
00085     
00086     while (true) {
00087         int ip_len = 15;
00088         
00089         if (!(command("ip=wifi.sta.getip();") &&
00090               command("print(ip)") &&
00091               execute(_ip, &ip_len)))
00092             return false;
00093         
00094         _ip[ip_len] = 0;
00095         
00096         if (strcmp(_ip, "nil") != 0)
00097             return true;
00098         
00099         if (timer.read_ms() > _timeout)
00100             return false;
00101     }
00102 }
00103 
00104 bool ESP8266::disconnect() {
00105     int ip_len = 15;
00106     
00107     if (!(command("wifi.sta.disconnect();") &&
00108           command("ip=wifi.sta.getip();") &&
00109           command("print(ip)") &&
00110           execute(_ip, &ip_len)))
00111         return false;
00112         
00113     _ip[ip_len] = 0;
00114     
00115     return (strcmp(_ip, "nil") == 0);
00116 }
00117 
00118 bool ESP8266::is_connected() {
00119     return (strcmp(_ip, "nil") != 0);
00120 }
00121 
00122 bool ESP8266::open(bool type, char* ip, int port, int id) {
00123     // Create the actual connection
00124     if (!(command("c=net.createConnection(") &&
00125           command(type ? "net.TCP" : "net.UDP") &&
00126           command(")") &&
00127           execute()))
00128         return false;
00129         
00130     // Setup handlers for connecting and disconnecting
00131     if (!(command("cc=nil;") &&
00132           command("c:on('connection',function() cc=true end);") &&
00133           command("c:on('disconnection',function() cc=false end)") &&
00134           execute()))
00135         return false;
00136     
00137     // Setup functions for sending and recieving characters on the esp side
00138     if (!(command("cm='';") &&
00139           command("function cs(n) c:send(n) end;") &&
00140           command("function ca() print(#cm) end;") &&
00141           command("function cr(n) " 
00142                     "d=cm:sub(1,n):gsub('.', function(s) "
00143                       "return s.format('\\\\%03d', s:byte(1)) "
00144                     "end);"
00145                     "cm=cm:sub(n+1,-1);" 
00146                     "print(d) "
00147                   "end;") && 
00148           command("c:on('receive',function(c,n) cm=cm..n end)") && 
00149           execute()))
00150         return false;
00151     
00152     // Convert port to a string    
00153     char port_buf[16];
00154     sprintf(port_buf, "%d", port);
00155         
00156     // Connect to the ip address
00157     if (!(command("c:connect(") &&
00158           command(port_buf) &&
00159           command(",'") &&
00160           command(ip) &&
00161           command("')") &&
00162           execute()))
00163         return false;
00164         
00165     // Wait for it to connect
00166     // TODO WISHLIST make this a seperate check so it can run asynch?
00167     Timer timer;
00168     timer.start();
00169     
00170     while (true) {
00171         char con_buf[5];
00172         int con_len = 5;
00173         
00174         if (!(command("print(cc)") && execute(con_buf, &con_len)))
00175             return false;
00176         
00177         if (memcmp(con_buf, "true", con_len) == 0)
00178             return true;
00179         else if (memcmp(con_buf, "false", con_len) == 0)
00180             return false;
00181         
00182         if (timer.read_ms() > _timeout)
00183             return false;
00184     }
00185 }
00186     
00187 bool ESP8266::close() {
00188     return command("c:close();" "c=nil") && execute();
00189 }
00190 
00191 bool ESP8266::send(const char *buffer, int len) {     
00192     for (int line = 0; line < len; line += ESP_MAX_LINE) {
00193         if (!command("cs('"))
00194             return false;
00195 
00196         for (int i = 0; i < ESP_MAX_LINE && line+i < len; i++) {
00197             int a = buffer[line+i] / 100;
00198             int b = (buffer[line+i] - a*100) / 10;
00199             int c = (buffer[line+i] - a*100 - b*10);
00200             
00201             if (serialputc('\\') < 0 ||
00202                 serialputc(a + '0') < 0 ||
00203                 serialputc(b + '0') < 0 ||
00204                 serialputc(c + '0') < 0)
00205                 return false;
00206         }
00207         
00208         if (!(command("')") && execute()))
00209             return false;
00210     }
00211     
00212     return true;
00213 }
00214 
00215 bool ESP8266::recv(char *buffer, int *len) {
00216     char len_buf[16];
00217     sprintf(len_buf, "%d", *len);
00218     
00219     if (!(command("cr(") &&
00220           command(len_buf) &&
00221           command(")")))
00222         return false;
00223         
00224    if (!(command("\r\n") && discardEcho()))
00225         return false;
00226         
00227     // Read in response
00228     for (int i = 0; i < *len; i++) {
00229         int e = serialgetc();
00230         
00231         if (e == '\r') {
00232             *len = i;
00233             break;
00234         } else if (e != '\\') {
00235             return false;
00236         }
00237         
00238         int a = serialgetc();
00239         int b = serialgetc();
00240         int c = serialgetc();
00241         
00242         if (a < 0 || b < 0 || c < 0)
00243             return false;
00244             
00245         buffer[i] = (a-'0')*100 + (b-'0')*10 + (c-'0');
00246     }
00247     
00248     // Flush to next prompt
00249     return flush();
00250 }
00251 
00252 int ESP8266::putc(char c) {
00253     char buffer[1] = { c };
00254     
00255     return send(buffer, 1) ? 0 : -1;
00256 }
00257 
00258 int ESP8266::getc() {
00259     char buffer[1];
00260     
00261     while (true) {
00262         int len = 1;
00263         
00264         if (!recv(buffer, &len))
00265             return -1;
00266             
00267         if (len > 0)
00268             break;
00269     }
00270     
00271     return buffer[0];
00272 }
00273 
00274 int ESP8266::writeable() {
00275     // Always succeeds since message can be temporarily stored on the esp
00276     return 1;
00277 }
00278 
00279 int ESP8266::readable() {
00280     char count_buf[16];
00281     int count_len = 15;
00282     
00283     if (!(command("ca()") && execute(count_buf, &count_len)))
00284         return false;
00285         
00286     count_buf[count_len] = 0;
00287     return atoi(count_buf);
00288 }   
00289 
00290 const char *ESP8266::getIPAddress() {
00291     if (strcmp(_ip, "nil") != 0)
00292         return _ip;
00293     else
00294         return 0;
00295 }
00296 
00297 bool ESP8266::getHostByName(const char *host, char *ip) {
00298     if (!(command("dh='") && 
00299           command(host) &&
00300           command("';") &&
00301           command("dn=nil;") &&
00302           command("if dh:match('%d+.%d+.%d+.%d+') then ") &&
00303           command("dn=dh ") &&
00304           command("else ") &&
00305           command("dc=net.createConnection(net.TCP);") &&
00306           command("dc:dns(dh,function(dc,ip) dn=ip end);") &&
00307           command("dc=nil ") &&
00308           command("end") &&
00309           execute()))
00310         return false;
00311 
00312     // Wait for a response
00313     Timer timer;
00314     timer.start();
00315     
00316     while (true) {
00317         int ip_len = 15;
00318         
00319         if (!(command("print(dn)") && execute(ip, &ip_len)))
00320             return false;
00321             
00322         ip[ip_len] = 0;
00323             
00324         if (strcmp(ip, "nil") != 0)
00325             return true;
00326         
00327         if (timer.read_ms() > _timeout)
00328             return false;
00329     }
00330 }
00331 
00332 int ESP8266::serialgetc() {
00333     Timer timer;
00334     timer.start();
00335     
00336     while (true) {
00337         if (_serial.readable()) {
00338             char c = _serial.getc();
00339 #ifdef ESP8266_ECHO
00340             printf("%c", c);
00341 #endif
00342             return c;
00343         }
00344             
00345         if (timer.read_ms() > _timeout)
00346             return -1;
00347     }
00348 }
00349 
00350 int ESP8266::serialputc(char c) {
00351     Timer timer;
00352     timer.start();
00353     
00354     while (true) {
00355         if (_serial.writeable())
00356             return _serial.putc(c);
00357             
00358         if (timer.read_ms() > _timeout)
00359             return -1;
00360     }
00361 }
00362 
00363 bool ESP8266::discardEcho() {
00364     while (true) {
00365         int c = serialgetc();
00366         
00367         if (c < 0)
00368             return false;
00369         else if (c == '\n')
00370             return true;
00371     }
00372 }
00373 
00374 bool ESP8266::flush() {
00375     while (true) {
00376         int c = serialgetc();
00377         
00378         if (c < 0)
00379             return false;
00380         else if (c == '>')
00381             return true;
00382     }
00383 }
00384 
00385 bool ESP8266::command(const char *cmd) {
00386     DBG("command sent:\t %s", cmd);
00387     
00388     for (int i = 0; cmd[i]; i++) {
00389         if (serialputc(cmd[i]) < 0)
00390             return false;
00391     }
00392     
00393     return true;
00394 } 
00395 
00396 bool ESP8266::execute(char *resp_buf, int *resp_len) {
00397     // Finish command with a newline
00398     if (!(command("\r\n") && discardEcho()))
00399         return false;
00400         
00401     // Read in response if any
00402     if (resp_buf && resp_len) {
00403         int i;
00404         
00405         for (i = 0; i < *resp_len; i++) {
00406             int c = serialgetc();
00407                 
00408             if (c < 0)
00409                 return false;
00410             
00411             if (c == '\r') {
00412                 *resp_len = i;
00413                 break;
00414             }
00415             
00416             resp_buf[i] = c;
00417         }
00418         
00419         DBG("command response:\t %.*s", *resp_len, resp_buf);
00420     }
00421     
00422     return flush();
00423 }