now this shit works

Dependencies:   BufferedSerial

Dependents:   IoTWeatherStation

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